package com.agent.management.service.impl;

import com.agent.management.common.exception.BusinessException;
import com.agent.management.engine.DagResolver;
import com.agent.management.engine.NodeTypeUtils;
import com.agent.management.model.entity.Workflow;
import com.agent.management.parser.WorkflowNameNormalizer;
import com.agent.management.repository.WorkflowRepository;
import com.agent.management.service.WorkflowService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class WorkflowServiceImpl implements WorkflowService {

    private final WorkflowRepository workflowRepo;

    @Override
    public List<Workflow> listWorkflows() {
        return workflowRepo.findAllByOrderByUpdatedAtDesc();
    }

    @Override
    public Workflow getWorkflow(String nameOrId) {
        Workflow wf = workflowRepo.findByName(nameOrId).orElse(null);
        if (wf != null) return wf;
        // 回退：若 nameOrId 是数字字符串，按 id 查找（兼容旧 URL/调用方）
        if (nameOrId != null && nameOrId.matches("\\d+")) {
            return workflowRepo.findById(Long.valueOf(nameOrId))
                    .orElseThrow(() -> new BusinessException("工作流不存在: " + nameOrId));
        }
        throw new BusinessException("工作流不存在: " + nameOrId);
    }

    @Override
    public Workflow getWorkflowById(Long id) {
        return workflowRepo.findById(id)
                .orElseThrow(() -> new BusinessException("工作流不存在: " + id));
    }

    @Override
    public Workflow createWorkflow(String name, String displayName, String description, String mode) {
        String normalizedName = WorkflowNameNormalizer.normalizeStrict(name);
        if (workflowRepo.existsByNameExcluding(normalizedName, null)) {
            throw new BusinessException("工作流名称已存在: " + normalizedName);
        }
        Workflow wf = new Workflow();
        wf.setName(normalizedName);
        wf.setDisplayName(displayName != null && !displayName.isBlank() ? displayName : normalizedName);
        wf.setDescription(description);
        wf.setMode("AGENTIC".equalsIgnoreCase(mode) ? "AGENTIC" : "DAG");
        wf.setGraphData("{\"nodes\":[],\"edges\":[]}");
        wf.onCreate();
        return workflowRepo.save(wf);
    }

    @Override
    public Workflow updateWorkflow(String nameOrId, String newName, String displayName,
                                   String description, String graphData, String mode) {
        Workflow wf = getWorkflow(nameOrId);
        // name 处理：若提供则规范化 + 唯一性校验
        if (newName != null && !newName.isBlank()) {
            String normalized = WorkflowNameNormalizer.normalizeStrict(newName);
            if (!normalized.equals(wf.getName())
                    && workflowRepo.existsByNameExcluding(normalized, wf.getId())) {
                throw new BusinessException("工作流名称已存在: " + normalized);
            }
            wf.setName(normalized);
        }
        if (displayName != null) wf.setDisplayName(displayName);
        if (description != null) wf.setDescription(description);
        if (graphData != null) {
            String normalized = NodeTypeUtils.normalizeGraphData(graphData);
            validateWorkflowGraph(normalized);
            wf.setGraphData(normalized);
        }
        if (mode != null) wf.setMode("AGENTIC".equalsIgnoreCase(mode) ? "AGENTIC" : "DAG");
        wf.onUpdate();
        return workflowRepo.save(wf);
    }

    /**
     * 校验工作流图结构：必须包含输入/输出节点，且输入输出需通过其他节点连通。
     */
    private void validateWorkflowGraph(String graphData) {
        if (graphData == null || graphData.isBlank()) {
            throw new BusinessException("工作流图数据不能为空");
        }

        DagResolver.ResolvedDag dag = DagResolver.resolve(graphData);
        Map<String, String> nodeTypeMap = dag.getNodeTypeMap();

        List<String> inputNodes = new ArrayList<>();
        List<String> outputNodes = new ArrayList<>();
        for (Map.Entry<String, String> entry : nodeTypeMap.entrySet()) {
            String type = entry.getValue();
            if ("userInput".equals(type)) {
                inputNodes.add(entry.getKey());
            } else if ("output".equals(type)) {
                outputNodes.add(entry.getKey());
            }
        }

        if (inputNodes.isEmpty()) {
            throw new BusinessException("工作流必须包含至少一个用户输入节点");
        }
        if (outputNodes.isEmpty()) {
            throw new BusinessException("工作流必须包含至少一个输出节点");
        }

        // 要求从某个输入节点到某个输出节点存在长度 >= 2 的路径（中间至少经过 1 个其他节点）
        boolean hasConnectedPath = false;
        for (String inputId : inputNodes) {
            Set<String> reachable = computeReachable(inputId, dag.getOutgoingEdges());
            for (String outputId : outputNodes) {
                if (reachable.contains(outputId) && hasIntermediateNode(inputId, outputId, dag.getOutgoingEdges(), nodeTypeMap)) {
                    hasConnectedPath = true;
                    break;
                }
            }
            if (hasConnectedPath) break;
        }

        if (!hasConnectedPath) {
            throw new BusinessException("用户输入节点与输出节点必须通过其他节点连接起来");
        }
    }

    /**
     * 计算从 start 出发能到达的所有节点（含自身）。
     */
    private Set<String> computeReachable(String start, Map<String, List<DagResolver.EdgeInfo>> outgoingEdges) {
        Set<String> visited = new HashSet<>();
        Deque<String> stack = new ArrayDeque<>();
        stack.push(start);
        while (!stack.isEmpty()) {
            String current = stack.pop();
            if (!visited.add(current)) continue;
            for (DagResolver.EdgeInfo edge : outgoingEdges.getOrDefault(current, Collections.emptyList())) {
                if (!visited.contains(edge.getTarget())) {
                    stack.push(edge.getTarget());
                }
            }
        }
        return visited;
    }

    /**
     * 判断从 start 到 end 是否存在至少一条长度 >= 2 的路径（中间经过其他节点）。
     */
    private boolean hasIntermediateNode(String start, String end,
                                        Map<String, List<DagResolver.EdgeInfo>> outgoingEdges,
                                        Map<String, String> nodeTypeMap) {
        // 直接相连的边不算满足条件
        for (DagResolver.EdgeInfo edge : outgoingEdges.getOrDefault(start, Collections.emptyList())) {
            if (edge.getTarget().equals(end)) {
                return false;
            }
        }

        // 检查是否存在一条 start -> X -> ... -> end 的路径，其中 X 不是输出节点
        Set<String> visited = new HashSet<>();
        Deque<String> stack = new ArrayDeque<>();
        for (DagResolver.EdgeInfo first : outgoingEdges.getOrDefault(start, Collections.emptyList())) {
            String next = first.getTarget();
            if (!"output".equals(nodeTypeMap.get(next))) {
                stack.push(next);
            }
        }

        while (!stack.isEmpty()) {
            String current = stack.pop();
            if (!visited.add(current)) continue;
            if (current.equals(end)) return true;
            for (DagResolver.EdgeInfo edge : outgoingEdges.getOrDefault(current, Collections.emptyList())) {
                String target = edge.getTarget();
                if (!visited.contains(target)) {
                    stack.push(target);
                }
            }
        }
        return false;
    }

    @Override
    public Workflow updateCategory(String nameOrId, Long categoryId) {
        Workflow wf = getWorkflow(nameOrId);
        wf.setCategoryId(categoryId);
        wf.onUpdate();
        return workflowRepo.save(wf);
    }

    @Override
    public void deleteWorkflow(String nameOrId) {
        Workflow wf = getWorkflow(nameOrId);
        workflowRepo.deleteById(wf.getId());
    }
}
