package com.agent.management.engine.executor;

import com.agent.management.engine.NodeExecutionResult;
import com.agent.management.engine.NodeExecutor;
import com.agent.management.engine.NodeInputResolver;
import com.agent.management.engine.NodeOutputEnvelope;
import com.agent.management.engine.NodeWorkspace;
import com.agent.management.engine.WorkflowContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 输出节点执行器
 *
 * <p>语义：输出节点按画布声明的 {@code data.fields[]} 进行投影输出，每个 field 既是从前驱取值的"输入变量"，
 * 也是写入最终结果的"输出变量"。投影逻辑复用 {@link NodeInputResolver}，享受与 LLM/Agent/Skill 节点
 * 一致的能力：显式 mapping 优先 + 四级隐式回退（精确同名 → 忽略 -_/大小写 → JSON 路径精确 → JSON 路径模糊），
 * 以及按声明 type 的类型转换。</p>
 *
 * <ul>
 *   <li>{@code data.fields[]} 每一项作为输入字段解析：field.name 同时作为输出 key（裸名，不带 predId__ 前缀）</li>
 *   <li>未声明 fields：返回空 data（用户决策：严格语义）</li>
 *   <li>required 字段缺失：返回 failure envelope</li>
 * </ul>
 *
 * <p>注意：工作空间文件列表（{@code _workingDirFiles}）和运行 ID（{@code _runId}）已不再注入 envelope.data，
 * 改由 WorkflowEngine 在运行完成时作为 WorkflowRun 的属性持久化，前端从 run 对象上直接读取。</p>
 */
@Slf4j
@Component
public class OutputExecutor implements NodeExecutor {

    private static final ObjectMapper MAPPER = new ObjectMapper();

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

    @Override
    public NodeExecutionResult execute(String nodeId, JsonNode data, WorkflowContext context, NodeWorkspace workspace) {
        String outputType = data.path("outputType").asText("text");

        // 按 data.fields 投影：把 fields 当作 inputs 解析（复用 NodeInputResolver 的 mapping + 四级模糊匹配 + 类型转换）
        Map<String, Object> projectedData = new LinkedHashMap<>();
        JsonNode fieldsNode = data.path("fields");
        if (fieldsNode.isArray() && fieldsNode.size() > 0) {
            // 包装成 {inputs: [...fields]} 复用 NodeInputResolver 的解析路径
            ObjectNode fieldsAsInputs = MAPPER.createObjectNode();
            fieldsAsInputs.set("inputs", fieldsNode);
            NodeInputResolver.ResolvedInputs resolved =
                    NodeInputResolver.resolveInputs(fieldsAsInputs, workspace);

            if (resolved.hasMissing()) {
                String msg = "输出节点缺少必填字段：" + String.join("、", resolved.getMissing());
                log.warn("[Output] 节点 {} 必填校验失败：{}", nodeId, resolved.getMissing());
                return NodeExecutionResult.success(nodeId,
                        NodeOutputEnvelope.failure(msg).toMap());
            }

            if (!resolved.getWarnings().isEmpty()) {
                resolved.getWarnings().forEach(w ->
                        log.warn("[Output] 节点 {} 输入解析警告: {}", nodeId, w));
            }

            projectedData.putAll(resolved.getValues());
        }
        // 未声明 fields：projectedData 保持为空（严格语义）

        log.info("[Output] 节点 {} 输出类型={}, 变量数={}", nodeId, outputType, projectedData.size());
        return NodeExecutionResult.success(nodeId,
                NodeOutputEnvelope.success("工作流执行成功", projectedData).toMap());
    }
}
