package cn.fateverse.workflow.service;

import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.workflow.entity.HistoricalOperation;
import cn.fateverse.workflow.entity.bo.AnalysisProcessBo;
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
import cn.fateverse.workflow.enums.OperationStateEnums;
import cn.fateverse.workflow.enums.ProcessNodeEnum;
import com.alibaba.fastjson2.JSON;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.engine.runtime.ActivityInstance;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 流程引擎服务
 *
 * @author Clay
 * @date 2023-03-20
 */
public class ProcessService {

    //流程节点id的映射集合
    private Map<String, ProcessNode> processNodeIdMap;
    //流程节点父级id映射集合
    private Map<String, ProcessNode> processNodeParentIdMap;
    //已经通过的节点信息
    private Set<String> agreeNodeSet;
    //运行中的节点信息
    private Set<String> runningNodeSet;
    //需要改变的节点信息
    private Set<String> changeNodeSet;
    //离开网关的节点信息
    private Set<String> leaveMergeNodeSet;
    //进入网关的节点信息
    private Set<String> enterMergeNodeSet;
    //需要回滚的网关信息
    private Set<String> rollbackNodeSet;
    //节点经过的路径
    private Set<String> nodePathSet;

    private Boolean findTargetNode = false;

    public void clear() {
        this.processNodeIdMap = null;
        this.processNodeParentIdMap = null;
        this.agreeNodeSet = null;
        this.runningNodeSet = null;
        this.leaveMergeNodeSet = null;
        this.enterMergeNodeSet = null;
        this.changeNodeSet = null;
        this.rollbackNodeSet = null;
        this.findTargetNode = false;
        this.nodePathSet = null;
    }


    /**
     * 获取到可以到达的节点
     *
     * @param operationList 操作历史节点
     * @param processStr    流程json数据
     * @param nodeId        节点id
     * @return 可以到达的节点list
     */
    public List<ProcessNode> analysisEndOption(List<HistoricalOperation> operationList, String processStr, String nodeId) {
        AnalysisProcessBo processData = initAnalysisProcessData(operationList, processStr);
        //需要返回的两三天
        List<ProcessNode> result = new ArrayList<>();
        //获取到当前nodeId对应的node数据
        processNodeIdMap = processData.getProcessNodeIdMap();
        processNodeParentIdMap = processData.getProcessNodeParentIdMap();
        nodePathSet = new HashSet<>();
        agreeNodeSet = processData.getHiOperationNodeSet();
        ProcessNode processNode = processNodeIdMap.get(nodeId);
        //开始分析
        doAnalysisEndOption(result, processNode);
        clear();
        return result;
    }

    /**
     * 分析节点流程
     *
     * @param result  返回list
     * @param oldNode 上一个节点信息
     */
    private void doAnalysisEndOption(List<ProcessNode> result, ProcessNode oldNode) {
        //获取到新的节点
        ProcessNode processNode = processNodeIdMap.get(oldNode.getParentId());
        //如果为空则直接返回,或者已经走过了直接返回
        if (null == processNode || nodePathSet.contains(processNode.getId())) {
            return;
        }
        nodePathSet.add(processNode.getId());
        //判断当前节点是否条件分支和并行分支
        if ((processNode.getType() == ProcessNodeEnum.CONDITIONS && oldNode.getType() == ProcessNodeEnum.EMPTY)
                || (processNode.getType() == ProcessNodeEnum.CONCURRENTS && oldNode.getType() == ProcessNodeEnum.MERGE)) {
            //是则需要处理当前分支下面的节点信息
            doGetBranchApproval(processNode, result, false);
        }
        //如果是用户,则直接放入到返回节点列表
        if (processNode.getType() == ProcessNodeEnum.APPROVAL) {
            result.add(processNode);
        }
        //递归调用当前函数
        doAnalysisEndOption(result, processNode);
    }

    /**
     * 获取到分支节点下的数据信息
     *
     * @param processNode 当前的节点
     * @param result      返回结果
     */
    private void doGetBranchApproval(ProcessNode processNode, List<ProcessNode> result, Boolean check) {
        if ((null == processNode || nodePathSet.contains(processNode.getId())) && check) {
            return;
        }
        nodePathSet.add(processNode.getId());
        //如果是条件分支或者并行分支,则需要每一个分支进行递归处理
        if (processNode.getType() == ProcessNodeEnum.CONCURRENTS || processNode.getType() == ProcessNodeEnum.CONDITIONS) {
            for (ProcessNode branch : processNode.getBranchs()) {
                ProcessNode branchSun = processNodeParentIdMap.get(branch.getId());
                doGetBranchApproval(branchSun, result, true);
            }
            if (nodePathSet.contains(processNode.getId())) {
                return;
            } else {
                processNode = processNodeParentIdMap.get(processNode.getId());
            }
            //return;
            //如果是审批节点,则需要加入到返回结果集中
        } else if (processNode.getType() == ProcessNodeEnum.APPROVAL) {
            if (agreeNodeSet.contains(processNode.getId())) {
                result.add(processNode);
            }
        }
        //继续下一个节点
        ProcessNode sun = processNodeParentIdMap.get(processNode.getId());
        //递归调用
        doGetBranchApproval(sun, result, true);
    }

    /**
     * 分析节点中运行中的节点信息,用作回退时的回滚记录
     *
     * @param operationList 操作历史记录
     * @param processStr    流程数据Str
     * @param nodeId        当前节点id
     * @param targetNodeId  退回目标节点id
     * @return 分析完成后的结果信息
     */
    public AnalysisProcessBo analysisProcessNodeAndRunningNode(List<HistoricalOperation> operationList, String processStr, String nodeId,
                                                               String targetNodeId, List<ActivityInstance> activityInstanceList, Collection<FlowElement> flowElements) {
        if (null == activityInstanceList || activityInstanceList.isEmpty()) {
            throw new CustomException("数据异常!");
        }
        AnalysisProcessBo processData = initAnalysisProcessData(operationList, processStr);
        //获取到所有的
        agreeNodeSet = processData.getHiOperationNodeSet();
        //处理所有的流程实例
        Map<String, List<ActivityInstance>> activityInstanceMap = activityInstanceList.stream().collect(Collectors.groupingBy(ActivityInstance::getActivityId));
        //获取到所有的连线
        List<SequenceFlow> sequenceFlows = flowElements.stream().filter(element -> element instanceof SequenceFlow).map(element -> (SequenceFlow) element).collect(Collectors.toList());
        //将连线通过源接地那id进行映射
        Map<String, List<SequenceFlow>> sequenceFlowSourceRefMap = sequenceFlows.stream().collect(Collectors.groupingBy(SequenceFlow::getSourceRef));
        //初始化所有运行状态的节点
        runningNodeSet = operationList.stream()
                .filter(operation -> operation.getState().equals(OperationStateEnums.RUNNING) && operation.getMark().equals(0))
                .map(HistoricalOperation::getNodeId).collect(Collectors.toSet());
        //id与流程节点的映射关系
        processNodeIdMap = processData.getProcessNodeIdMap();
        //parentId和流程节点的映射关系
        processNodeParentIdMap = processData.getProcessNodeParentIdMap();
        //需要改变的节点
        changeNodeSet = new HashSet<>();
        //需要回滚的节点
        rollbackNodeSet = new HashSet<>();
        //进入网关的节点信息 (网关关闭节点)
        leaveMergeNodeSet = new HashSet<>();
        //退出网关的节点信息 (网关关闭节点)
        enterMergeNodeSet = new HashSet<>();
        //将当然节点加入到回滚节点列表中
        rollbackNodeSet.add(nodeId);
        //将目标节点加入到需要改变的节点列表中
        changeNodeSet.add(targetNodeId);
        //分析需要删除的操作历史和当前正在运行的实例
        Set<String> activityIdSet = new HashSet<>();
        analysisActivityId(activityInstanceMap, sequenceFlowSourceRefMap, activityIdSet, targetNodeId);
        //筛选出㤇改变和回滚的接地那列表
        doAnalysisProcessNodeAndRunning(sequenceFlowSourceRefMap, targetNodeId);
        //获取到当前节点
        ProcessNode processNode = processNodeIdMap.get(nodeId);
        //分析正在运行的接地那流程,筛选出需要进出网关的情况
        doAnalysisProcessNodeAndGateway(processNode, targetNodeId);
        //将数据打包返回
        AnalysisProcessBo result = AnalysisProcessBo.builder()
                .changeNodeSet(changeNodeSet)
                .rollbackNodeSet(rollbackNodeSet)
                .leaveMergeNodeSet(leaveMergeNodeSet)
                .enterMergeNodeSet(enterMergeNodeSet)
                .activityIdSet(activityIdSet)
                .build();
        clear();
        return result;
    }

    /**
     * 获取到从退回节点到需要退回节点的所有节点状态,统计需要修改的节点,和需要回滚的节点信息
     * (正向寻找)
     *
     * @param sequenceFlowSourceRefMap 连线映射
     * @param targetNodeId             目标节点
     */
    private void doAnalysisProcessNodeAndRunning(Map<String, List<SequenceFlow>> sequenceFlowSourceRefMap, String targetNodeId) {
        //获取到连线
        List<SequenceFlow> sequenceFlows = sequenceFlowSourceRefMap.get(targetNodeId);
        if (null == sequenceFlows) {
            return;
        }
        //遍历连线
        for (SequenceFlow sequenceFlow : sequenceFlows) {
            //获取到当前线条目标节点
            ProcessNode processNode = processNodeIdMap.get(sequenceFlow.getTargetRef());
            if (null == processNode) {
                continue;
            }
            //判断目标接地那是否是审批节点,抄送节点,延时等待节点,触发器
            if (processNode.getType() == ProcessNodeEnum.APPROVAL || processNode.getType() == ProcessNodeEnum.CC
                    || processNode.getType() == ProcessNodeEnum.DELAY || processNode.getType() == ProcessNodeEnum.TRIGGER) {
                //判断当前节点是否在运行集合或者同意集合中
                if (!(runningNodeSet.contains(processNode.getId()) || agreeNodeSet.contains(processNode.getId()))) {
                    continue;
                }
                //加入到可以到达的集合中
                processNodeAdded(processNode);
            }
            //进行递归
            doAnalysisProcessNodeAndRunning(sequenceFlowSourceRefMap, processNode.getId());
        }
    }


    private void analysisActivityId(Map<String, List<ActivityInstance>> activityInstanceMap,
                                    Map<String, List<SequenceFlow>> sequenceFlowSourceRefMap,
                                    Set<String> activityIdSet, String activityId) {
        //获取连线
        List<SequenceFlow> sequenceFlows = sequenceFlowSourceRefMap.get(activityId);
        if (null == sequenceFlows) {
            return;
        }
        //遍历连线
        for (SequenceFlow sequenceFlow : sequenceFlows) {
            //获取到当前连线的目标节点
            List<ActivityInstance> activityInstanceList = activityInstanceMap.get(sequenceFlow.getTargetRef());
            if (null == activityInstanceList || activityInstanceList.isEmpty()) {
                continue;
            }
            //不为空则需要加入到集合中
            for (ActivityInstance activityInstance : activityInstanceList) {
                activityIdSet.add(activityInstance.getId());
            }
            //获取到连线的实例
            List<ActivityInstance> sequenceInstanceList = activityInstanceMap.get(sequenceFlow.getId());
            //不为空加入到集合中
            if (null != sequenceInstanceList && !sequenceInstanceList.isEmpty()) {
                for (ActivityInstance sequenceInstance : sequenceInstanceList) {
                    activityIdSet.add(sequenceInstance.getId());
                }
            }
            //进行递归
            analysisActivityId(activityInstanceMap, sequenceFlowSourceRefMap, activityIdSet, activityInstanceList.get(0).getActivityId());
        }

    }

    /**
     * 获取到并行网关出入情况 (逆向寻找,从当前节点逆推到目标节点)
     *
     * @param oldNode      历史节点
     * @param targetNodeId 目标节点id
     */
    private void doAnalysisProcessNodeAndGateway(ProcessNode oldNode, String targetNodeId) {
        ProcessNode processNode = processNodeIdMap.get(oldNode.getParentId());
        //如果为空则直接返回
        if (null == processNode) {
            return;
        }
        //如果当前获取到的接地那等于目标节点,则说明找到,打上找到了的标记进行返回
        if (processNode.getId().equals(targetNodeId)) {
            findTargetNode = true;
            return;
        }
        //判断当前节点是否条件分支和并行分支, 进入网关 从 MERGE 走向 CONCURRENTS 则说明是从网关外部进入,则说明需要进入网关
        if (processNode.getType() == ProcessNodeEnum.CONCURRENTS && oldNode.getType() == ProcessNodeEnum.MERGE) {
            //处理分支下面的数据信息
            for (ProcessNode branch : processNode.getBranchs()) {
                if (!findTargetNode) {
                    ProcessNode branchSun = processNodeParentIdMap.get(branch.getId());
                    doAnalysisProcessAndGatewayBranch(branchSun, targetNodeId);
                }
            }
            //
            setEnterMergeNodeSet(processNode.getId());
        }
        if (findTargetNode) {
            return;
        }
        //判断当前节点是否条件分支和并行分支 从 CONCURRENT 到 CONCURRENTS 则说明是出网关
        if (processNode.getType() == ProcessNodeEnum.CONCURRENTS && oldNode.getType() == ProcessNodeEnum.CONCURRENT) {
            setLeaveMergeNodeSet(processNode.getId());
        }
        doAnalysisProcessNodeAndGateway(processNode, targetNodeId);
    }

    /**
     * 获取到 并行网关分支的进入情况
     *
     * @param processNode  流程节点数据
     * @param targetNodeId 目标id
     */
    private void doAnalysisProcessAndGatewayBranch(ProcessNode processNode, String targetNodeId) {
        //结果是否为空,为空则跳出递归
        if (null == processNode) {
            return;
        }
        if (processNode.getId().equals(targetNodeId)) {
            findTargetNode = true;
            return;
        }
        //判断当前节点是否存在于agree 或 这running中,如果不存在,则表示未激活
        if (!(agreeNodeSet.contains(processNode.getId()) || runningNodeSet.contains(processNode.getId()))
                && (processNode.getType() == ProcessNodeEnum.CC || processNode.getType() == ProcessNodeEnum.TRIGGER
                || processNode.getType() == ProcessNodeEnum.APPROVAL)) {
            return;
        }
        //如果是条件分支或者并行分支,则需要每一个分支进行递归处理 分支中有网关节点,则说明是进入网关
        if (processNode.getType() == ProcessNodeEnum.CONCURRENTS || processNode.getType() == ProcessNodeEnum.CONDITIONS) {
            for (ProcessNode branch : processNode.getBranchs()) {
                if (!findTargetNode) {
                    ProcessNode branchSun = processNodeParentIdMap.get(branch.getId());
                    doAnalysisProcessAndGatewayBranch(branchSun, targetNodeId);
                }
            }
            if (processNode.getType() == ProcessNodeEnum.CONCURRENTS) {
                setEnterMergeNodeSet(processNode.getId());
            }
            //如果是审批节点,则需要加入到返回结果集中
        }
        if (findTargetNode) {
            return;
        }
        //继续下一个节点
        ProcessNode sun = processNodeParentIdMap.get(processNode.getId());
        //递归调用
        doAnalysisProcessAndGatewayBranch(sun, targetNodeId);
    }

    /**
     * 添加出网关的分支节点
     *
     * @param nodeId 节点id
     */
    private void setLeaveMergeNodeSet(String nodeId) {
        ProcessNode mergeNode = processNodeParentIdMap.get(nodeId);
        leaveMergeNodeSet.add(mergeNode.getId());
    }

    /**
     * 添加进去分支网关的节点
     *
     * @param nodeId 节点id
     */
    private void setEnterMergeNodeSet(String nodeId) {
        ProcessNode mergeNode = processNodeParentIdMap.get(nodeId);
        enterMergeNodeSet.add(mergeNode.getId());
    }


    /**
     * 添加流程节点信息
     *
     * @param processNode 流程节点
     */
    private void processNodeAdded(ProcessNode processNode) {
        //在agree中
        if (agreeNodeSet.contains(processNode.getId())) {
            changeNodeSet.add(processNode.getId());
            //在running中
        } else if (runningNodeSet.contains(processNode.getId())) {
            rollbackNodeSet.add(processNode.getId());
        }
    }

    /**
     * 初始化流程bo的数据
     *
     * @param operationList 操作历史
     * @param processStr    流程节点数据Str
     * @return 流程分析业务数据
     */
    private AnalysisProcessBo initAnalysisProcessData(List<HistoricalOperation> operationList, String processStr) {
        //获取到所有的
        Set<String> hiOperationNodeSet = operationList.stream()
                .filter(operation -> (operation.getState().equals(OperationStateEnums.AGREE) || operation.getState().equals(OperationStateEnums.AUTO_PASS)) && operation.getMark().equals(0))
                .map(HistoricalOperation::getNodeId).collect(Collectors.toSet());
        //分支id映射关系
        Map<String, ProcessNode> branchNodeIdMap = new HashMap<>();
        //解析流程JSON
        List<ProcessNode> processNodes = JSON.parseArray(processStr, ProcessNode.class);
        //parentId和流程节点的映射关系
        Map<String, ProcessNode> processNodeParentIdMap = new HashMap<>(processNodes.size());
        //id与流程节点的映射关系
        Map<String, ProcessNode> processNodeIdMap = processNodes.stream()
                .peek(processNode -> {
                    if (processNode.getType() == ProcessNodeEnum.CONCURRENTS || processNode.getType() == ProcessNodeEnum.CONDITIONS) {
                        Map<String, ProcessNode> nodeMap = processNode.getBranchs().stream().collect(Collectors.toMap(ProcessNode::getId, Function.identity()));
                        branchNodeIdMap.putAll(nodeMap);
                    }
                    processNodeParentIdMap.put(processNode.getParentId(), processNode);
                })
                .collect(Collectors.toMap(ProcessNode::getId, Function.identity()));
        //将分支节点的映射关系放入到id映射关系内
        processNodeIdMap.putAll(branchNodeIdMap);
        return AnalysisProcessBo.builder()
                .hiOperationNodeSet(hiOperationNodeSet)
                .processNodeParentIdMap(processNodeParentIdMap)
                .processNodeIdMap(processNodeIdMap)
                .build();
    }

}
