package tech.easyflow.publicapi.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.publicapi.dto.PublicWorkflowTopology;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.PriorityQueue;
import java.util.Set;

/**
 * 从已发布工作流快照生成对外安全的节点拓扑。
 */
@Service
public class PublicWorkflowTopologyService {

    /**
     * 构建已发布工作流的公开拓扑视图。
     *
     * @param workflow 已发布工作流
     * @return 安全拓扑视图
     */
    public PublicWorkflowTopology resolve(Workflow workflow) {
        if (workflow == null || !StringUtils.hasText(workflow.getContent())) {
            throw new BusinessException("已发布工作流缺少拓扑内容");
        }
        JSONObject root = parseContent(workflow.getContent());
        JSONArray rawNodes = root.getJSONArray("nodes");
        JSONArray rawEdges = root.getJSONArray("edges");

        LinkedHashMap<String, NodeBuilder> nodeById =
                parseNodes(rawNodes);
        List<PublicWorkflowTopology.Edge> edges =
                parseEdges(rawEdges, nodeById);
        addLoopCompletionBarriers(nodeById);
        TopologyOrder topology = resolveTopology(nodeById);
        List<PublicWorkflowTopology.Node> nodes =
                buildNodes(nodeById, topology);

        return new PublicWorkflowTopology(
                workflow.getId() == null
                        ? null
                        : workflow.getId().toString(),
                trimToNull(workflow.getAlias()),
                trimToNull(workflow.getTitle()),
                trimToNull(workflow.getDescription()),
                workflow.getRevision(),
                workflow.getPublishedAt() == null
                        ? null
                        : workflow.getPublishedAt()
                        .toInstant()
                        .toString(),
                nodes,
                edges,
                topology.order,
                topology.levels,
                !topology.unresolvedNodeIds.isEmpty(),
                topology.unresolvedNodeIds);
    }

    /**
     * 解析工作流 JSON 内容。
     *
     * @param content 工作流 JSON
     * @return 根对象
     */
    private JSONObject parseContent(String content) {
        try {
            JSONObject root = JSON.parseObject(content);
            if (root == null) {
                throw new BusinessException("已发布工作流拓扑为空");
            }
            return root;
        } catch (JSONException error) {
            throw new BusinessException(
                    400,
                    40031,
                    "已发布工作流拓扑解析失败",
                    error);
        }
    }

    /**
     * 解析并校验节点定义。
     *
     * @param rawNodes 原始节点数组
     * @return 按发布定义顺序排列的节点构建器
     */
    private LinkedHashMap<String, NodeBuilder> parseNodes(
            JSONArray rawNodes) {
        if (rawNodes == null || rawNodes.isEmpty()) {
            throw new BusinessException("已发布工作流缺少节点");
        }
        LinkedHashMap<String, NodeBuilder> nodes =
                new LinkedHashMap<>();
        for (int index = 0; index < rawNodes.size(); index++) {
            JSONObject rawNode = rawNodes.getJSONObject(index);
            String nodeId = trimToNull(
                    rawNode == null ? null : rawNode.getString("id"));
            if (!StringUtils.hasText(nodeId)) {
                throw new BusinessException(
                        "已发布工作流存在缺少 ID 的节点");
            }
            if (nodes.containsKey(nodeId)) {
                throw new BusinessException(
                        "已发布工作流存在重复节点 ID: " + nodeId);
            }
            JSONObject data = rawNode.getJSONObject("data");
            String nodeType = trimToNull(rawNode.getString("type"));
            nodes.put(nodeId, new NodeBuilder(
                    nodeId,
                    nodeType,
                    firstText(
                            data == null
                                    ? null
                                    : data.getString("title"),
                            rawNode.getString("label"),
                            nodeId),
                    firstText(
                            data == null
                                    ? null
                                    : data.getString("description"),
                            rawNode.getString("description")),
                    trimToNull(rawNode.getString("parentId")),
                    index,
                    parseParameters(
                            data == null
                                    ? null
                                    : data.getJSONArray("parameters"),
                            "startNode".equals(nodeType)),
                    parseParameters(
                            data == null
                                    ? null
                                    : data.getJSONArray("outputDefs"),
                            false)));
        }
        return nodes;
    }

    /**
     * 解析公开边信息并建立节点邻接关系。
     *
     * @param rawEdges 原始边数组
     * @param nodeById 节点索引
     * @return 公开边列表
     */
    private List<PublicWorkflowTopology.Edge> parseEdges(
            JSONArray rawEdges,
            Map<String, NodeBuilder> nodeById) {
        if (rawEdges == null || rawEdges.isEmpty()) {
            return List.of();
        }
        List<PublicWorkflowTopology.Edge> edges =
                new ArrayList<>(rawEdges.size());
        for (int index = 0; index < rawEdges.size(); index++) {
            JSONObject rawEdge = rawEdges.getJSONObject(index);
            if (rawEdge == null) {
                continue;
            }
            JSONObject data = rawEdge.getJSONObject("data");
            String edgeId = firstText(
                    rawEdge.getString("id"),
                    "edge-" + index);
            String sourceId = trimToNull(
                    rawEdge.getString("source"));
            String targetId = trimToNull(
                    rawEdge.getString("target"));
            NodeBuilder source = nodeById.get(sourceId);
            NodeBuilder target = nodeById.get(targetId);
            boolean dangling = source == null || target == null;
            if (!dangling) {
                source.addOutgoing(edgeId, targetId);
                target.addIncoming(edgeId, sourceId);
                if (isSameScope(source, target)
                        || isLoopEntry(source, target)) {
                    addTopologyDependency(source, target);
                }
            }
            edges.add(new PublicWorkflowTopology.Edge(
                    edgeId,
                    trimToNull(rawEdge.getString("type")),
                    firstText(
                            rawEdge.getString("label"),
                            data == null
                                    ? null
                                    : data.getString("label")),
                    sourceId,
                    targetId,
                    trimToNull(rawEdge.getString("sourceHandle")),
                    trimToNull(rawEdge.getString("targetHandle")),
                    firstText(
                            data == null
                                    ? null
                                    : data.getString("parentNodeId"),
                            rawEdge.getString("parentId")),
                    index,
                    dangling));
        }
        return edges;
    }

    /**
     * 为循环体末节点与循环后的节点增加结构化完成屏障。
     *
     * <p>运行时由循环体末节点隐式返回父循环节点，父循环完成全部迭代后
     * 才会触发同作用域后继。公开拓扑使用末节点到后继的无环屏障表达这一约束。</p>
     *
     * @param nodeById 节点索引
     */
    private void addLoopCompletionBarriers(
            Map<String, NodeBuilder> nodeById) {
        for (NodeBuilder loopNode : nodeById.values()) {
            if (!"loopNode".equals(loopNode.nodeType)) {
                continue;
            }
            List<NodeBuilder> downstream = loopNode
                    .topologySuccessorNodeIds.stream()
                    .map(nodeById::get)
                    .filter(Objects::nonNull)
                    .filter(target -> isSameScope(loopNode, target))
                    .toList();
            if (downstream.isEmpty()) {
                continue;
            }
            for (NodeBuilder exitNode :
                    resolveLoopScopeExitNodes(
                            loopNode.nodeId,
                            nodeById,
                            new HashSet<>())) {
                for (NodeBuilder target : downstream) {
                    addTopologyDependency(exitNode, target);
                }
            }
        }
    }

    /**
     * 解析循环作用域完成时必须结束的叶子节点。
     *
     * <p>若作用域叶子本身是嵌套循环，则继续展开到嵌套循环体叶子，
     * 确保外层后继等待完整嵌套作用域结束。</p>
     *
     * @param loopNodeId 循环节点 ID
     * @param nodeById 节点索引
     * @param visiting 正在展开的循环节点
     * @return 实际完成屏障节点
     */
    private List<NodeBuilder> resolveLoopScopeExitNodes(
            String loopNodeId,
            Map<String, NodeBuilder> nodeById,
            Set<String> visiting) {
        if (!visiting.add(loopNodeId)) {
            return List.of();
        }
        List<NodeBuilder> exits = new ArrayList<>();
        for (NodeBuilder candidate : nodeById.values()) {
            if (!Objects.equals(loopNodeId, candidate.parentNodeId)
                    || hasSameScopeSuccessor(candidate, nodeById)) {
                continue;
            }
            if ("loopNode".equals(candidate.nodeType)) {
                List<NodeBuilder> nested = resolveLoopScopeExitNodes(
                        candidate.nodeId,
                        nodeById,
                        visiting);
                if (!nested.isEmpty()) {
                    exits.addAll(nested);
                    continue;
                }
            }
            exits.add(candidate);
        }
        visiting.remove(loopNodeId);
        return exits;
    }

    /**
     * 判断节点是否存在同一父级作用域内的后继。
     *
     * @param node 当前节点
     * @param nodeById 节点索引
     * @return 是否存在同作用域后继
     */
    private boolean hasSameScopeSuccessor(
            NodeBuilder node,
            Map<String, NodeBuilder> nodeById) {
        for (String successorId : node.topologySuccessorNodeIds) {
            NodeBuilder successor = nodeById.get(successorId);
            if (successor != null && isSameScope(node, successor)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断两个节点是否位于同一父级作用域。
     *
     * @param source 源节点
     * @param target 目标节点
     * @return 是否同作用域
     */
    private boolean isSameScope(
            NodeBuilder source,
            NodeBuilder target) {
        return Objects.equals(
                source.parentNodeId,
                target.parentNodeId);
    }

    /**
     * 判断连线是否为循环节点进入直属循环体的入口。
     *
     * @param source 源节点
     * @param target 目标节点
     * @return 是否循环入口
     */
    private boolean isLoopEntry(
            NodeBuilder source,
            NodeBuilder target) {
        return "loopNode".equals(source.nodeType)
                && source.nodeId.equals(target.parentNodeId);
    }

    /**
     * 登记拓扑排序使用的依赖关系。
     *
     * @param source 源节点
     * @param target 目标节点
     */
    private void addTopologyDependency(
            NodeBuilder source,
            NodeBuilder target) {
        if (source.topologySuccessorNodeIds.add(target.nodeId)) {
            target.topologyPredecessorNodeIds.add(source.nodeId);
        }
    }

    /**
     * 对节点执行稳定 Kahn 拓扑排序。
     *
     * <p>同一层级节点按发布定义顺序稳定排列。若发布图意外存在环，
     * 标准拓扑序之后按定义顺序追加受环路影响的未解析节点。</p>
     *
     * @param nodeById 节点索引
     * @return 拓扑排序结果
     */
    private TopologyOrder resolveTopology(
            LinkedHashMap<String, NodeBuilder> nodeById) {
        Map<String, Integer> remainingInDegree = new HashMap<>();
        Map<String, Integer> levelById = new HashMap<>();
        PriorityQueue<NodeBuilder> ready = new PriorityQueue<>(
                Comparator.comparingInt(node -> node.definitionIndex));
        for (NodeBuilder node : nodeById.values()) {
            remainingInDegree.put(
                    node.nodeId,
                    node.topologyPredecessorNodeIds.size());
            levelById.put(node.nodeId, 0);
            if (node.topologyPredecessorNodeIds.isEmpty()) {
                ready.add(node);
            }
        }

        List<String> order = new ArrayList<>(nodeById.size());
        Set<String> visited = new HashSet<>();
        while (!ready.isEmpty()) {
            NodeBuilder current = ready.poll();
            if (!visited.add(current.nodeId)) {
                continue;
            }
            order.add(current.nodeId);
            int nextLevel = levelById.getOrDefault(current.nodeId, 0) + 1;
            for (String successorId :
                    current.topologySuccessorNodeIds) {
                levelById.compute(
                        successorId,
                        (key, value) -> Math.max(
                                value == null ? 0 : value,
                                nextLevel));
            }
            for (String edgeTargetId :
                    current.topologySuccessorNodeIds) {
                int nextInDegree = remainingInDegree.computeIfPresent(
                        edgeTargetId,
                        (key, value) -> value - 1);
                if (nextInDegree == 0) {
                    ready.add(nodeById.get(edgeTargetId));
                }
            }
        }

        List<String> unresolvedNodeIds = new ArrayList<>();
        for (NodeBuilder node : nodeById.values()) {
            if (!visited.contains(node.nodeId)) {
                unresolvedNodeIds.add(node.nodeId);
                order.add(node.nodeId);
                levelById.put(node.nodeId, -1);
            }
        }
        return new TopologyOrder(
                order,
                buildLevels(order, levelById),
                unresolvedNodeIds,
                levelById);
    }

    /**
     * 构建可并行处理的拓扑层级。
     *
     * @param order 稳定拓扑顺序
     * @param levelById 节点层级
     * @return 按层级排列的节点 ID
     */
    private List<List<String>> buildLevels(
            List<String> order,
            Map<String, Integer> levelById) {
        int maxLevel = -1;
        for (String nodeId : order) {
            maxLevel = Math.max(
                    maxLevel,
                    levelById.getOrDefault(nodeId, -1));
        }
        if (maxLevel < 0) {
            return List.of();
        }
        List<List<String>> levels = new ArrayList<>(maxLevel + 1);
        for (int index = 0; index <= maxLevel; index++) {
            levels.add(new ArrayList<>());
        }
        for (String nodeId : order) {
            int level = levelById.getOrDefault(nodeId, -1);
            if (level >= 0) {
                levels.get(level).add(nodeId);
            }
        }
        return levels;
    }

    /**
     * 将内部节点构建器转换为公开节点 DTO。
     *
     * @param nodeById 节点索引
     * @param topology 拓扑排序结果
     * @return 按稳定拓扑顺序排列的公开节点
     */
    private List<PublicWorkflowTopology.Node> buildNodes(
            Map<String, NodeBuilder> nodeById,
            TopologyOrder topology) {
        List<PublicWorkflowTopology.Node> nodes =
                new ArrayList<>(topology.order.size());
        for (int topologyIndex = 0;
             topologyIndex < topology.order.size();
             topologyIndex++) {
            NodeBuilder node = nodeById.get(
                    topology.order.get(topologyIndex));
            nodes.add(new PublicWorkflowTopology.Node(
                    node.nodeId,
                    node.nodeType,
                    node.nodeName,
                    node.description,
                    node.parentNodeId,
                    node.definitionIndex,
                    topologyIndex,
                    topology.levelById.getOrDefault(node.nodeId, -1),
                    node.incomingEdgeIds.size(),
                    node.outgoingEdgeIds.size(),
                    "startNode".equals(node.nodeType),
                    "endNode".equals(node.nodeType),
                    new ArrayList<>(node.predecessorNodeIds),
                    new ArrayList<>(node.successorNodeIds),
                    node.incomingEdgeIds,
                    node.outgoingEdgeIds,
                    node.inputParameters,
                    node.outputParameters));
        }
        return nodes;
    }

    /**
     * 解析参数数组中的安全元数据。
     *
     * @param rawParameters 原始参数数组
     * @param multipartEnabled 是否为开始节点可上传输入参数
     * @return 参数安全元数据
     */
    private List<PublicWorkflowTopology.Parameter> parseParameters(
            JSONArray rawParameters,
            boolean multipartEnabled) {
        if (rawParameters == null || rawParameters.isEmpty()) {
            return List.of();
        }
        List<PublicWorkflowTopology.Parameter> parameters =
                new ArrayList<>(rawParameters.size());
        for (int index = 0; index < rawParameters.size(); index++) {
            JSONObject parameter = rawParameters.getJSONObject(index);
            if (parameter == null) {
                continue;
            }
            String parameterName = firstText(
                    parameter.getString("name"),
                    parameter.getString("key"));
            String dataType =
                    trimToNull(parameter.getString("dataType"));
            String contentType = firstText(
                    parameter.getString("contentType"),
                    parameter.getString("type"),
                    parameter.getString("formType"));
            parameters.add(new PublicWorkflowTopology.Parameter(
                    firstText(
                            parameter.getString("id"),
                            parameter.getString("key")),
                    parameterName,
                    firstText(
                            parameter.getString("formLabel"),
                            parameter.getString("label"),
                            parameter.getString("title"),
                            parameter.getString("name"),
                            parameter.getString("key")),
                    dataType,
                    contentType,
                    parameter.getBooleanValue("required"),
                    firstText(
                            parameter.getString("formDescription"),
                            parameter.getString("description")),
                    multipartEnabled
                            && isFileParameter(dataType, contentType)
                            && StringUtils.hasText(parameterName)
                            ? WorkflowApiMultipartParameterMapper
                            .FILE_PART_PREFIX + parameterName
                            : null));
        }
        return parameters;
    }

    /**
     * 判断公开参数元数据是否描述文件输入。
     *
     * @param dataType 参数数据类型
     * @param contentType 参数内容类型
     * @return 是否为文件参数
     */
    private boolean isFileParameter(
            String dataType,
            String contentType) {
        return "file".equalsIgnoreCase(dataType)
                || "file".equalsIgnoreCase(contentType);
    }

    /**
     * 返回首个非空文本。
     *
     * @param values 候选文本
     * @return 首个非空文本
     */
    private String firstText(String... values) {
        for (String value : values) {
            String normalized = trimToNull(value);
            if (normalized != null) {
                return normalized;
            }
        }
        return null;
    }

    /**
     * 去除文本首尾空白并把空串转换为空值。
     *
     * @param value 原文本
     * @return 归一化文本
     */
    private String trimToNull(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        return value.trim();
    }

    /**
     * 拓扑排序使用的内部节点。
     */
    private static final class NodeBuilder {

        private final String nodeId;
        private final String nodeType;
        private final String nodeName;
        private final String description;
        private final String parentNodeId;
        private final int definitionIndex;
        private final List<PublicWorkflowTopology.Parameter> inputParameters;
        private final List<PublicWorkflowTopology.Parameter> outputParameters;
        private final LinkedHashSet<String> predecessorNodeIds =
                new LinkedHashSet<>();
        private final LinkedHashSet<String> successorNodeIds =
                new LinkedHashSet<>();
        private final List<String> incomingEdgeIds = new ArrayList<>();
        private final List<String> outgoingEdgeIds = new ArrayList<>();
        private final LinkedHashSet<String>
                topologyPredecessorNodeIds = new LinkedHashSet<>();
        private final LinkedHashSet<String>
                topologySuccessorNodeIds = new LinkedHashSet<>();

        /**
         * 创建内部节点。
         *
         * @param nodeId 节点 ID
         * @param nodeType 节点类型
         * @param nodeName 节点名称
         * @param description 节点描述
         * @param parentNodeId 父节点 ID
         * @param definitionIndex 发布定义位置
         * @param inputParameters 输入参数
         * @param outputParameters 输出参数
         */
        private NodeBuilder(
                String nodeId,
                String nodeType,
                String nodeName,
                String description,
                String parentNodeId,
                int definitionIndex,
                List<PublicWorkflowTopology.Parameter> inputParameters,
                List<PublicWorkflowTopology.Parameter> outputParameters) {
            this.nodeId = nodeId;
            this.nodeType = nodeType;
            this.nodeName = nodeName;
            this.description = description;
            this.parentNodeId = parentNodeId;
            this.definitionIndex = definitionIndex;
            this.inputParameters = inputParameters;
            this.outputParameters = outputParameters;
        }

        /**
         * 登记一条有效出边。
         *
         * @param edgeId 边 ID
         * @param targetId 目标节点 ID
         */
        private void addOutgoing(String edgeId, String targetId) {
            outgoingEdgeIds.add(edgeId);
            successorNodeIds.add(targetId);
        }

        /**
         * 登记一条有效入边。
         *
         * @param edgeId 边 ID
         * @param sourceId 源节点 ID
         */
        private void addIncoming(String edgeId, String sourceId) {
            incomingEdgeIds.add(edgeId);
            predecessorNodeIds.add(sourceId);
        }
    }

    /**
     * 稳定拓扑排序内部结果。
     *
     * @param order 节点顺序
     * @param levels 拓扑层级
     * @param unresolvedNodeIds 受环路影响的未解析节点 ID
     * @param levelById 节点层级索引
     */
    private record TopologyOrder(
            List<String> order,
            List<List<String>> levels,
            List<String> unresolvedNodeIds,
            Map<String, Integer> levelById) {
    }
}
