package com.agent.management.engine;

import com.agent.management.external.SseEventBus;
import com.agent.management.model.entity.Workflow;
import com.agent.management.model.entity.WorkflowRun;
import com.agent.management.repository.WorkflowRepository;
import com.agent.management.repository.WorkflowRunNodeRepository;
import com.agent.management.repository.WorkflowRunRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 工作流调度引擎
 * 解析 DAG 图，按拓扑层级逐层执行节点，通过 SSE 推送实时状态
 */
@Slf4j
@Component
public class WorkflowEngine {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final WorkflowRepository workflowRepo;
    private final WorkflowRunRepository runRepo;
    private final WorkflowRunNodeRepository runNodeRepo;
    private final WorkflowRunDirManager runDirManager;
    private final WorkflowLevelExecutor levelExecutor;
    private final AgenticExecutor agenticExecutor;
    private final SseEventBus eventBus;
    private final AgentIgnoreFilter agentIgnoreFilter;
    private final ExecutorService executor = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors(),
            r -> {
                Thread t = new Thread(r, "workflow-engine");
                t.setDaemon(true);
                return t;
            }
    );

    public WorkflowEngine(WorkflowRepository workflowRepo,
                          WorkflowRunRepository runRepo, WorkflowRunNodeRepository runNodeRepo,
                          WorkflowRunDirManager runDirManager,
                          WorkflowLevelExecutor levelExecutor,
                          AgenticExecutor agenticExecutor,
                          SseEventBus eventBus,
                          AgentIgnoreFilter agentIgnoreFilter) {
        this.workflowRepo = workflowRepo;
        this.runRepo = runRepo;
        this.runNodeRepo = runNodeRepo;
        this.runDirManager = runDirManager;
        this.levelExecutor = levelExecutor;
        this.agenticExecutor = agenticExecutor;
        this.eventBus = eventBus;
        this.agentIgnoreFilter = agentIgnoreFilter;
        log.info("[WorkflowEngine] 已初始化，DAG 模式 → WorkflowLevelExecutor，AGENTIC 模式 → AgenticExecutor");
    }

    /**
     * 外部 API 入口：使用调用方指定的 runId 触发执行，事件通过 SseEventBus 派发。
     * 工作目录由调用方提前创建并填充文件，inputs 中不需要 _preRunId。
     *
     * <p>注：24h 兜底超时由 {@link WorkflowLevelExecutor#EXECUTION_TIMEOUT_MINUTES} 提供，
     * 本方法不再额外调度外层超时（原 {@code Long.MAX_VALUE} 计划因整数溢出无意义，已移除）。</p>
     */
    public void executeForExternal(Long workflowId, String runId, Map<String, Object> inputs) {
        eventBus.register(runId);
        executor.submit(() -> {
            try {
                execute(workflowId, runId, inputs, new NoopSseEmitter());
            } catch (Exception e) {
                log.error("[WorkflowEngine] 外部执行异常: {}", e.getMessage(), e);
                eventBus.publish(runId, "workflow_error", WorkflowRunEvent.workflowError(runId, e.getMessage()));
            }
        });
    }

    /**
     * 异步执行工作流
     */
    public void executeAsync(Long workflowId, Map<String, Object> inputs, SseEmitter emitter) {
        // 支持预生成的 runId（文件上传场景：Controller 已提前创建工作目录）
        String runId;
        if (inputs != null && inputs.containsKey("_preRunId")) {
            runId = (String) inputs.remove("_preRunId");
        } else {
            runId = UUID.randomUUID().toString().substring(0, 8);
        }
        final String finalRunId = runId;
        executor.submit(() -> {
            try {
                execute(workflowId, finalRunId, inputs, emitter);
            } catch (Exception e) {
                log.error("[WorkflowEngine] 执行异常: {}", e.getMessage(), e);
                safeSend(emitter, WorkflowRunEvent.workflowError(finalRunId, e.getMessage()));
                emitter.completeWithError(e);
            }
        });
    }

    /**
     * 同步执行工作流（内部方法）
     */
    private void execute(Long workflowId, String runId, Map<String, Object> inputs, SseEmitter emitter) {
        log.info("[WorkflowEngine] 开始执行 workflowId={}, runId={}", workflowId, runId);

        // 1. 加载工作流
        Workflow wf = workflowRepo.findById(workflowId)
                .orElseThrow(() -> new IllegalArgumentException("工作流不存在: " + workflowId));

        String graphData = wf.getGraphData();
        if (graphData == null || graphData.equals("{\"nodes\":[],\"edges\":[]}")) {
            safeSend(emitter, WorkflowRunEvent.workflowError(runId, "工作流图为空"));
            emitter.complete();
            return;
        }

        // 2. 解析 DAG
        DagResolver.ResolvedDag dag = DagResolver.resolve(graphData);
        log.info("[WorkflowEngine] DAG 解析完成: {} 个节点, {} 层", dag.getNodeDataMap().size(), dag.getLevels().size());

        // 3. 创建运行记录
        WorkflowRun runRecord = new WorkflowRun();
        runRecord.setWorkflowId(workflowId);
        runRecord.setWorkflowName(wf.getName());
        runRecord.setRunId(runId);
        runRecord.setStatus("RUNNING");
        try {
            runRecord.setInputs(inputs != null ? MAPPER.writeValueAsString(inputs) : "{}");
        } catch (JsonProcessingException e) {
            runRecord.setInputs("{}");
        }
        runRecord = runRepo.save(runRecord);

        // 4. 创建工作目录（按 name 命名）
        java.nio.file.Path workingDir = runDirManager.createRunDir(workflowId, wf.getName(), runId);

        // 5. 初始化上下文
        WorkflowContext context = new WorkflowContext(workflowId, runId, workingDir, inputs);

        // 6. 委托执行器：DAG 模式走拓扑层级；AGENTIC 模式走 AI 决策链
        //    对应设计文档 docs/plans/agentic-workflow-plan.md §2.1
        boolean isAgentic = "AGENTIC".equalsIgnoreCase(wf.getMode());
        WorkflowLevelExecutor.ExecutionOutcome outcome;
        try {
            if (isAgentic) {
                outcome = agenticExecutor.execute(dag, context, runId, runRecord.getId(), emitter);
            } else {
                outcome = levelExecutor.executeByLevel(dag, context, runId, runRecord.getId(), emitter);
            }
        } catch (Exception e) {
            // 引擎内部异常：标记为 FAILED 并持久化部分节点记录
            log.error("[WorkflowEngine] executor 抛出异常: {}", e.getMessage(), e);
            safeSend(emitter, WorkflowRunEvent.workflowError(runId, e.getMessage()));
            persistRunResult(runRecord, new WorkflowLevelExecutor.ExecutionOutcome(
                    new LinkedHashMap<>(), new ArrayList<>(), true, e.getMessage()), null);
            emitter.complete();
            return;
        }

        // 7. 扫描工作目录文件列表（成功时才有意义；失败时跳过）
        List<String> workingDirFiles = outcome.failed ? null : scanWorkingDirFiles(workingDir);

        // 8. 持久化 + 推送完成/错误事件
        persistRunResult(runRecord, outcome, workingDirFiles);
        if (outcome.failed) {
            emitter.complete();
            return;
        }
        safeSend(emitter, WorkflowRunEvent.workflowComplete(runId, outcome.finalOutputs, workingDirFiles));
        emitter.complete();
        log.info("[WorkflowEngine] 执行完成, runId={}, 输出变量数={}", runId, outcome.finalOutputs.size());
    }

    /**
     * 把执行结果回写到 WorkflowRun + 节点记录表。
     * workingDirFiles 仅在成功时由调用方扫描后传入，作为 run 自身的属性持久化（不再注入 outputs JSON）。
     */
    private void persistRunResult(WorkflowRun runRecord, WorkflowLevelExecutor.ExecutionOutcome outcome,
                                  List<String> workingDirFiles) {
        runRecord.setStatus(outcome.failed ? "FAILED" : "SUCCESS");
        runRecord.setCompletedAt(LocalDateTime.now());
        if (outcome.failed) {
            runRecord.setError(outcome.errorMsg);
        } else {
            try {
                runRecord.setOutputs(outcome.finalOutputs.isEmpty() ? null : MAPPER.writeValueAsString(outcome.finalOutputs));
            } catch (JsonProcessingException e) {
                log.warn("[WorkflowEngine] 序列化输出失败", e);
            }
            if (workingDirFiles != null && !workingDirFiles.isEmpty()) {
                try {
                    runRecord.setWorkingDirFiles(MAPPER.writeValueAsString(workingDirFiles));
                } catch (JsonProcessingException e) {
                    log.warn("[WorkflowEngine] 序列化工作空间文件列表失败", e);
                }
            }
        }
        runRepo.save(runRecord);
        runNodeRepo.saveAll(outcome.nodeRecords);
    }

    /**
     * 扫描工作目录下应保留的文件列表（应用 .agentignore 过滤），返回相对路径（正斜杠）。
     * 与原 OutputExecutor 中实现的过滤语义一致：跳过目录自身、命中 .agentignore 的文件及祖先目录命中规则。
     */
    private List<String> scanWorkingDirFiles(Path workingDir) {
        if (workingDir == null || !Files.isDirectory(workingDir)) return Collections.emptyList();
        try {
            return Files.walk(workingDir)
                    .filter(p -> shouldInclude(workingDir, p))
                    .map(p -> workingDir.relativize(p).toString().replace('\\', '/'))
                    .collect(Collectors.toList());
        } catch (IOException e) {
            log.warn("[WorkflowEngine] 扫描工作目录失败: {}", e.getMessage());
            return Collections.emptyList();
        }
    }

    private boolean shouldInclude(Path workingDir, Path p) {
        if (Files.isDirectory(p)) return false;
        Path rel = workingDir.relativize(p);
        String relStr = rel.toString().replace('\\', '/');
        if (agentIgnoreFilter.shouldIgnore(relStr, false)) return false;
        Path cur = rel;
        while (cur != null && cur.getNameCount() > 1) {
            cur = cur.getParent();
            if (cur == null) break;
            if (agentIgnoreFilter.shouldIgnore(cur.toString().replace('\\', '/'), true)) return false;
        }
        return true;
    }

    /**
     * 安全发送 SSE 事件
     */
    private void safeSend(SseEmitter emitter, WorkflowRunEvent event) {
        // 同时发布到事件总线（外部 API 通过 /stream 订阅消费）
        eventBus.publish(event.getRunId(), event.getType(), event);
        try {
            emitter.send(SseEmitter.event()
                    .name(event.getType())
                    .data(event));
        } catch (IOException e) {
            log.warn("[WorkflowEngine] SSE 推送失败: {}", e.getMessage());
        }
    }
}
