package com.agent.management.engine.executor;

import com.agent.management.config.HermesProperties;
import com.agent.management.config.WorkflowProperties;
import com.agent.management.engine.*;
import com.agent.management.engine.hermes.HermesBridgeClient;
import com.agent.management.model.dto.IOField;
import com.agent.management.model.dto.SkillDTO;
import com.agent.management.model.entity.AiModel;
import com.agent.management.service.AiModelService;
import com.agent.management.service.SkillService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Hermes Agent 节点执行器
 * 将 Agent 节点的执行委托给 Hermes Bridge（Python AIAgent），
 * 获得 40+ 工具（终端、浏览器、搜索等）、Skill 系统和记忆系统。
 *
 * 仅在 hermes.enabled=true 时激活，替换原 AgentExecutor。
 */
@Slf4j
@Component
@ConditionalOnProperty(name = "hermes.enabled", havingValue = "true")
public class HermesAgentExecutor implements NodeExecutor {

    private final HermesBridgeClient bridgeClient;
    private final SkillService skillService;
    private final HermesProperties hermesProperties;
    private final AiModelService aiModelService;
    private final ChatClient.Builder fallbackBuilder;
    private final WorkflowProperties workflowProperties;

    public HermesAgentExecutor(HermesBridgeClient bridgeClient, SkillService skillService,
                               HermesProperties hermesProperties, AiModelService aiModelService,
                               ChatClient.Builder fallbackBuilder,
                               WorkflowProperties workflowProperties) {
        this.bridgeClient = bridgeClient;
        this.skillService = skillService;
        this.hermesProperties = hermesProperties;
        this.aiModelService = aiModelService;
        this.fallbackBuilder = fallbackBuilder;
        this.workflowProperties = workflowProperties;
    }

    @Override
    public String getType() {
        return "agent";
    }

    @Override
    public NodeExecutionResult execute(String nodeId, JsonNode data, WorkflowContext context, NodeWorkspace workspace) {
        String agentId = data.path("agentId").asText("");
        if (agentId.isEmpty()) {
            // C: 业务失败使用外层 FAILED，引擎按 failStrategy 处理
            return NodeExecutionResult.failed(nodeId, "Agent 节点未关联 Skill");
        }

        // 加载 Skill 内容作为系统提示
        String systemPrompt;
        String userMessage;
        try {
            SkillDTO skill = skillService.getSkill(agentId);
            // 把节点明确定义的输入变量追加到 SKILL.md 内容之后，作为完整系统提示
            systemPrompt = ContextPromptHelper.merge(skillService.getSkillFullContent(agentId), data, workspace);
            userMessage = buildUserMessage(skill, workspace);
        } catch (Exception e) {
            return NodeExecutionResult.failed(nodeId, "加载 Skill 失败: " + e.getMessage());
        }

        int maxIterations = data.path("maxIterations").asInt(hermesProperties.getBridge().getDefaultMaxIterations());
        JsonNode outputsDecl = data.path("outputs");
        boolean structured = StructuredOutputHelper.needsStructuredOutput(data);

        // 结构化模式：把 JSON 输出指令追加到 userMessage 末尾，让 Hermes 首次输出尽量就是合法 JSON，
        // 减少后续 reformat LLM 调用与 Hermes 重跑概率
        String fullUserMessage = userMessage;
        if (structured) {
            fullUserMessage = userMessage + StructuredOutputHelper.buildInstruction(outputsDecl, null);
        }

        log.info("[HermesAgent] 节点 {} 开始执行，Skill={}, maxIterations={}, structured={}",
                nodeId, agentId, maxIterations, structured);

        try {
            // hermesHome 传 null：由 HermesBridgeClient 回落到固定 HERMES_HOME（hermes.bridge.home）
            String workingDir = context.getWorkingDir() != null ? context.getWorkingDir().toString() : null;
            String sessionId = context.getRunId() + "_" + nodeId;
            Map<String, String> modelConfig = resolveModelConfig(data);
            HermesRunResult runResult = bridgeClient.run(systemPrompt, fullUserMessage, maxIterations,
                    null, workingDir, nodeId, context.getStreamSink(), sessionId, modelConfig);

            if (runResult.getFinalText() == null || runResult.getFinalText().isBlank()) {
                // C: 返回空结果视为业务失败
                return NodeExecutionResult.failed(nodeId, "Hermes Agent 返回空结果", runResult.getLogs());
            }

            log.info("[HermesAgent] 节点 {} 完成, 结果长度={}, 日志数={}",
                    nodeId, runResult.getFinalText().length(), runResult.getLogs().size());

            // 结构化模式：交给 extractWithRetryFromText 处理
            // - 内层用廉价 reformatClient 整理 JSON（首选）
            // - 内层耗尽时由 supplier 复用 sessionId 让 Hermes 重跑（cost 高，保底）
            if (structured) {
                ChatClient reformatClient = resolveReformatChatClient(data);
                RetryPolicy policy = RetryPolicy.forHermes(data, workflowProperties);

                Supplier<String> runtimeRetrySupplier = () -> {
                    try {
                        String retryMsg = "上次响应未通过结构化校验，请基于 outputs 声明严格重新生成纯 JSON，"
                                + "仅输出 JSON 本身，不要任何额外文字。";
                        HermesRunResult r = bridgeClient.run(systemPrompt, retryMsg, maxIterations,
                                null, workingDir, nodeId, context.getStreamSink(), sessionId, modelConfig);
                        return r == null ? "" : r.getFinalText();
                    } catch (Exception ex) {
                        // supplier 不允许抛 checked exception，包装为 RuntimeException 让上层记录
                        throw new RuntimeException("Hermes 重跑失败: " + ex.getMessage(), ex);
                    }
                };

                StructuredOutputHelper.ExtractionResult ext =
                        StructuredOutputHelper.extractWithRetryFromText(
                                runResult.getFinalText(), outputsDecl, policy, reformatClient, runtimeRetrySupplier);

                if (ext.errorMessage != null) {
                    log.warn("[HermesAgent] 节点 {} 结构化输出失败（累计 {} 次调用）: {}",
                            nodeId, ext.attempts, ext.errorMessage);
                    return NodeExecutionResult.failed(nodeId,
                            "Hermes Agent 结构化输出失败: " + ext.errorMessage, runResult.getLogs());
                }

                String msg = "Hermes Agent 执行成功" + (ext.attempts > 1 ? "（重试 " + (ext.attempts - 1) + " 次）" : "");
                log.info("[HermesAgent] 节点 {} 结构化输出完成, 字段数={}, attempts={}",
                        nodeId, ext.data.size(), ext.attempts);
                return NodeExecutionResult.success(nodeId,
                        NodeOutputEnvelope.success(msg, ext.data).toMap(), runResult.getLogs());
            }

            // 单输出降级模式：未声明 outputs，按默认变量名写一个字段
            String varName = NodeTypeUtils.resolveOutputVarName(data, "result");
            java.util.Map<String, Object> dataMap = new java.util.LinkedHashMap<>();
            dataMap.put(varName, runResult.getFinalText());
            return NodeExecutionResult.success(nodeId,
                    NodeOutputEnvelope.success("Hermes Agent 执行成功", dataMap).toMap(), runResult.getLogs());

        } catch (Exception e) {
            log.error("[HermesAgent] 节点 {} 调用失败: {}", nodeId, e.getMessage());
            // C: 外层状态与业务结果对齐 —— 异常一律 FAILED，由引擎统一构造失败 envelope 写入 context
            // 若 e 是 HermesErrorResponseException（方案 B 嗅探到的错误响应），getMessage 已包含「命中模式 / 原始响应」上下文
            return NodeExecutionResult.failed(nodeId, "Hermes Agent 调用失败: " + e.getMessage());
        }
    }

    /**
     * 根据节点 data 中的 modelId 解析模型配置。
     * 解析顺序：
     *   1. 节点显式指定的 modelId（>0）
     *   2. UI 当前默认模型（ai-models.json 中 isDefault=true）
     *   3. 返回 null，由 Bridge 使用环境变量默认配置
     *
     * 与 LlmExecutor.resolveChatClient 行为对齐，确保 UI 切换默认模型对 Hermes 节点立即生效。
     */
    private Map<String, String> resolveModelConfig(JsonNode data) {
        AiModel model = null;
        long modelId = data.path("modelId").asLong(0);
        if (modelId > 0) {
            try {
                model = aiModelService.getModel(modelId);
            } catch (Exception e) {
                log.warn("[HermesAgent] 获取指定模型 {} 失败，回退到默认模型: {}", modelId, e.getMessage());
            }
        }
        if (model == null) {
            model = aiModelService.getDefaultModel();
        }
        if (model == null) {
            log.warn("[HermesAgent] 无可用默认模型，回退到 Bridge 启动环境变量");
            return null;
        }
        if (model.getBaseUrl() == null || model.getBaseUrl().isBlank()
                || model.getApiKey() == null || model.getApiKey().isBlank()
                || model.getModelName() == null || model.getModelName().isBlank()) {
            log.warn("[HermesAgent] 模型 {} 配置不完整，回退到 Bridge 启动环境变量", model.getId());
            return null;
        }
        return Map.of(
                "modelId", String.valueOf(model.getId()),
                "baseUrl", model.getBaseUrl(),
                "apiKey", model.getApiKey(),
                "modelName", model.getModelName()
        );
    }

    /**
     * 解析结构化输出重组用的 ChatClient（内层 reformat 专用，廉价 LLM）。
     *
     * <p>优先级：</p>
     * <ol>
     *   <li>节点 {@code data.reformatModelId}（用户指定的廉价重组模型，推荐用低成本模型降低开销）</li>
     *   <li>节点 {@code data.modelId}（与推理模型共用，避免额外配置）</li>
     *   <li>UI 当前默认模型（确保 UI 切换默认模型对重组路径也生效）</li>
     *   <li>应用启动时注入的 fallbackBuilder</li>
     * </ol>
     */
    private ChatClient resolveReformatChatClient(JsonNode data) {
        long reformatModelId = data.path("reformatModelId").asLong(0);
        if (reformatModelId > 0) {
            try {
                return aiModelService.getChatClient(reformatModelId);
            } catch (Exception e) {
                log.warn("[HermesAgent] 获取重组模型 {} 失败，回退到推理模型: {}",
                        reformatModelId, e.getMessage());
            }
        }
        long modelId = data.path("modelId").asLong(0);
        if (modelId > 0) {
            try {
                return aiModelService.getChatClient(modelId);
            } catch (Exception e) {
                log.warn("[HermesAgent] 获取推理模型 {} 作为重组模型失败，回退到默认模型: {}",
                        modelId, e.getMessage());
            }
        }
        return aiModelService.getChatClientOrFallback(fallbackBuilder);
    }

    /**
     * 根据 Skill 的 inputs 定义，从工作区构造结构化用户消息
     */
    private String buildUserMessage(SkillDTO skill, NodeWorkspace workspace) {
        StringBuilder sb = new StringBuilder();

        if (skill.getInputs() != null && !skill.getInputs().isEmpty()) {
            for (IOField input : skill.getInputs()) {
                Object value = workspace.getVariable(input.getName());
                if (value != null) {
                    sb.append(input.getName()).append(": ").append(value).append("\n");
                }
            }
        }

        if (sb.isEmpty() && !workspace.getVariables().isEmpty()) {
            sb.append(workspace.getVariables().entrySet().stream()
                    .filter(e -> e.getValue() != null)
                    .map(e -> e.getKey() + ": " + e.getValue())
                    .collect(Collectors.joining("\n")));
        }

        return sb.toString();
    }
}
