package com.supervision.handler.graph;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.supervision.ngbatis.dao.CommonQueryDao;
import com.supervision.ngbatis.domain.tag.Condition;
import com.supervision.ngbatis.domain.tag.ProcessCondition;
import lombok.extern.slf4j.Slf4j;
import org.jgrapht.Graph;
import org.jgrapht.GraphPath;
import org.jgrapht.alg.shortestpath.AllDirectedPaths;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.builder.GraphTypeBuilder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 寻找判断节点的链条
 */
@Slf4j
@Component
public class FindConditionPathHandler {

    @Resource
    private CommonQueryDao commonQueryDao;

    /**
     * 根据某个办理条件节点,寻找所有路径,以用来判断是否符合
     * @param itemLeafId 叶子节点的ID
     * @return 所有路径
     */
    public List<List<Condition>> findConditionPath(String itemLeafId) {
        // 首先根据叶子节点,找到条件节点
        ProcessCondition conditionVertexByLeafVertex = commonQueryDao.findConditionVertexByLeafVertex(itemLeafId);
        if (ObjUtil.isEmpty(conditionVertexByLeafVertex)){
            return new ArrayList<>();
        }
        // 构建一个图,用这个图来找属性结构
        Graph<Condition, DefaultEdge> jGraph = buildJGraph();
        // 定位终点的vertexId
        Set<Condition> finalVertexSet = new HashSet<>();
        // 从条件节点processConditionVid开始探索下一级节点列表,使用GO语句
        List<Condition> sourceConditionList = commonQueryDao.findNextConditionByConditionEdge(conditionVertexByLeafVertex.getId());
        // 如果没有任何绑定的条件节点,
        if (CollUtil.isEmpty(sourceConditionList)) {
            // TODO 如果没有任何绑定的条件节点,这里咋整?暂定返回一个空的集合
            return new ArrayList<>();
        }
        for (Condition sourceVertex : sourceConditionList) {
            jGraph.addVertex(sourceVertex);
        }
        // 下面开始使用递归,开始持续的找下级节点(递归增加次数限制,最多100次)
        int recursionCount = 0;
        recursionFindNext(sourceConditionList, jGraph, finalVertexSet, recursionCount);
        // 根据图去找所有的路径
        return findAllPath(jGraph, new HashSet<>(sourceConditionList), finalVertexSet);

    }

    private void recursionFindNext(List<Condition> sourceIdList, Graph<Condition, DefaultEdge> jGraph, Set<Condition> finalVertexSet, int recursionCount) {
        // 防止死循环出现无限递归
        if (recursionCount > 100) {
            return;
        }
        recursionCount++;
        for (Condition source : sourceIdList) {
            List<Condition> nextVertxList = commonQueryDao.findNextConditionByConditionEdge(source.getId());
            if (CollUtil.isNotEmpty(nextVertxList)) {
                for (Condition nextVertex : nextVertxList) {
                    jGraph.addVertex(nextVertex);
                    jGraph.addEdge(nextVertex, nextVertex);
                }
                recursionFindNext(nextVertxList, jGraph, finalVertexSet, recursionCount);
            } else {
                finalVertexSet.add(source);
            }
        }
    }


    private Graph<Condition, DefaultEdge> buildJGraph() {
        // 创建一个树形结构
        return GraphTypeBuilder
                .directed()
                .allowingMultipleEdges(false)
                .allowingSelfLoops(false)
                .edgeClass(DefaultEdge.class)
                .vertexClass(Condition.class)
                .weighted(false)
                .buildGraph();
    }

    private List<List<Condition>> findAllPath(Graph<Condition, DefaultEdge> graph, Set<Condition> sourceSet, Set<Condition> targetIdSet) {
        // 使用深度优先遍历来获取所有路径
        AllDirectedPaths<Condition, DefaultEdge> directedPaths = new AllDirectedPaths<>(graph);
        // 根据一个起始点和终止节点,开始找所有的路径
        List<GraphPath<Condition, DefaultEdge>> allPaths = directedPaths.getAllPaths(sourceSet, targetIdSet, true, 100);
        // 将所有判断的路径汇聚起来
        List<List<Condition>> paths = new ArrayList<>();
        for (GraphPath<Condition, DefaultEdge> path : allPaths) {
            List<Condition> vertexList = path.getVertexList();
            if (CollUtil.isNotEmpty(vertexList)) {
                paths.add(vertexList);
            }
        }
        return paths;
    }
}