package dev.sonpx.loyalty.agent.core.executor;

import dev.sonpx.loyalty.agent.core.classifier.Intent;
import dev.sonpx.loyalty.agent.core.memory.AgentMemoryManager;
import dev.sonpx.loyalty.agent.core.orchestrator.PromptBuilder;
import dev.sonpx.loyalty.agent.domain.ChatRequest;
import dev.sonpx.loyalty.agent.service.AgentEventPublisher;
import dev.sonpx.loyalty.agent.service.AgentPersistenceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * Single-pass ReAct agent executor.
 * Replaces the old multi-task loop with a single LLM call that can use tools.
 * Spring AI handles the ReAct loop internally (tool_call → tool_result → next action).
 *
 * Key improvements over the old AgentExecutor:
 * - User prompt is always passed correctly (not hardcoded "Execute task.")
 * - Memory advisor is always attached
 * - Streams response from the start (no waiting for intermediate tasks)
 * - Error events always sent to user
 */
@Slf4j
@Component
public class SimpleAgentExecutor {

    private final ChatClient queryChatClient;
    private final ChatClient chatChatClient;
    private final ToolInterceptor toolInterceptor;
    private final AgentEventPublisher eventPublisher;
    private final AgentMemoryManager memoryManager;
    private final PromptBuilder promptBuilder;
    private final AgentPersistenceService persistenceService;

    public SimpleAgentExecutor(@Qualifier("queryAgentChatClient") ChatClient queryChatClient,
                               @Qualifier("chatAgentChatClient") ChatClient chatChatClient,
                               ToolInterceptor toolInterceptor,
                               AgentEventPublisher eventPublisher,
                               AgentMemoryManager memoryManager,
                               PromptBuilder promptBuilder,
                               AgentPersistenceService persistenceService) {
        this.queryChatClient = queryChatClient;
        this.chatChatClient = chatChatClient;
        this.toolInterceptor = toolInterceptor;
        this.eventPublisher = eventPublisher;
        this.memoryManager = memoryManager;
        this.promptBuilder = promptBuilder;
        this.persistenceService = persistenceService;
    }

    /**
     * Execute a request with tools (for QUERY and UNKNOWN intents).
     * The LLM decides which tools to call via ReAct pattern.
     */
    public void executeWithTools(ChatRequest request, String connectionSessionId, String conversationKey, Intent intent) {
        try {
            List<ToolCallback> wrappedTools = toolInterceptor.getWrappedTools(
                    connectionSessionId, request.messageId(), AgentToolScope.READ_ONLY);

        String systemPrompt = promptBuilder.buildSystemPrompt(intent);
        String userPrompt = promptBuilder.buildUserPrompt(request);

        log.info("SimpleAgent executing with tools for conversation: {}, connection: {}, intent: {}", conversationKey, connectionSessionId, intent);
        log.debug("System prompt: {}", systemPrompt);

        persistenceService.saveUserMessage(conversationKey, request.messageId(), request.prompt());
        StringBuilder responseAccumulator = new StringBuilder();

        queryChatClient.prompt()
                .advisors(memoryManager.getAdvisor())
                .advisors(a -> a
                        .param("chat_memory_conversation_id", conversationKey)
                        .param("chat_memory_response_size", 50))
                .system(systemPrompt)
                .user(userPrompt)
                .tools(wrappedTools)
                .stream()
                .content()
                .doOnComplete(() -> {
                    log.info("SimpleAgent execution completed for conversation: {}, connection: {}", conversationKey, connectionSessionId);
                    persistenceService.saveAssistantMessage(conversationKey, responseAccumulator.toString());
                    eventPublisher.done(connectionSessionId, request.messageId());
                })
                .subscribe(
                        content -> {
                            if (!content.isEmpty()) {
                                responseAccumulator.append(content);
                                eventPublisher.token(connectionSessionId, request.messageId(), content);
                            }
                        },
                        error -> {
                            log.error("Error during SimpleAgent execution for connection {}: {}", connectionSessionId, error.getMessage(), error);
                            String errorMessage = "Xin lỗi, đã xảy ra lỗi khi xử lý yêu cầu. Vui lòng thử lại.";
                            if (isConnectionError(error)) {
                                errorMessage = "Hiện tại dịch vụ AI (Ollama) đang tạm thời gián đoạn. Vui lòng thử lại sau.";
                            }
                            eventPublisher.error(connectionSessionId, request.messageId(), errorMessage);
                        }
                );
        } catch (Exception e) {
            log.error("Error setting up SimpleAgent for connection {}: {}", connectionSessionId, e.getMessage(), e);
            String errorMessage = "Xin lỗi, không thể khởi tạo tool. Vui lòng thử lại.";
            if (isConnectionError(e)) {
                errorMessage = "Hiện tại dịch vụ tool (MCP) đang tạm thời gián đoạn. Vui lòng thử lại sau.";
            }
            eventPublisher.error(connectionSessionId, request.messageId(), errorMessage);
        }
    }

    private boolean isConnectionError(Throwable t) {
        if (t == null) return false;
        Throwable current = t;
        while (current != null) {
            String name = current.getClass().getName();
            if (name.contains("ConnectException") || name.contains("SocketException") || name.contains("ResourceAccessException")) {
                return true;
            }
            current = current.getCause();
        }
        return false;
    }

    /**
     * Execute a direct chat without tools (for CONVERSATION intent).
     * No tools are loaded → faster response, less token usage.
     */
    public void executeDirectChat(ChatRequest request, String connectionSessionId, String conversationKey) {
        String systemPrompt = promptBuilder.buildSystemPrompt(Intent.CONVERSATION);
        String userPrompt = promptBuilder.buildUserPrompt(request);

        log.info("DirectChat executing for conversation: {}, connection: {}", conversationKey, connectionSessionId);

        persistenceService.saveUserMessage(conversationKey, request.messageId(), request.prompt());
        StringBuilder responseAccumulator = new StringBuilder();

        chatChatClient.prompt()
                .advisors(memoryManager.getAdvisor())
                .advisors(a -> a
                        .param("chat_memory_conversation_id", conversationKey)
                        .param("chat_memory_response_size", 50))
                .system(systemPrompt)
                .user(userPrompt)
                .stream()
                .content()
                .doOnComplete(() -> {
                    log.info("DirectChat completed for conversation: {}, connection: {}", conversationKey, connectionSessionId);
                    persistenceService.saveAssistantMessage(conversationKey, responseAccumulator.toString());
                    eventPublisher.done(connectionSessionId, request.messageId());
                })
                .subscribe(
                        content -> {
                            if (!content.isEmpty()) {
                                responseAccumulator.append(content);
                                eventPublisher.token(connectionSessionId, request.messageId(), content);
                            }
                        },
                        error -> {
                            log.error("Error during DirectChat execution for connection {}: {}", connectionSessionId, error.getMessage(), error);
                            String errorMessage = "Xin lỗi, đã xảy ra lỗi. Vui lòng thử lại.";
                            if (isConnectionError(error)) {
                                errorMessage = "Hiện tại dịch vụ AI (Ollama) đang tạm thời gián đoạn. Vui lòng thử lại sau.";
                            }
                            eventPublisher.error(connectionSessionId, request.messageId(), errorMessage);
                        }
                );
    }
}
