package cn.stylefeng.workflow.instance.modular.manage.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.entity.BaseEntity;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.roses.kernel.sys.api.SysUserServiceApi;
import cn.stylefeng.roses.kernel.sys.api.pojo.user.SimpleUserDTO;
import cn.stylefeng.workflow.api.DefinitionApi;
import cn.stylefeng.workflow.api.NodeApi;
import cn.stylefeng.workflow.api.entity.design.Definition;
import cn.stylefeng.workflow.api.entity.design.Model;
import cn.stylefeng.workflow.api.entity.design.Node;
import cn.stylefeng.workflow.api.entity.design.NodeEdge;
import cn.stylefeng.workflow.api.entity.instance.RuntimeInstance;
import cn.stylefeng.workflow.api.entity.instance.RuntimeNode;
import cn.stylefeng.workflow.api.entity.task.RuTask;
import cn.stylefeng.workflow.api.enums.NodeStatusEnum;
import cn.stylefeng.workflow.api.enums.NodeTypeEnum;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import cn.stylefeng.workflow.api.pojo.InstanceVariable;
import cn.stylefeng.workflow.api.pojo.config.node.NodeData;
import cn.stylefeng.workflow.api.pojo.config.node.approver.ApproverConfig;
import cn.stylefeng.workflow.api.pojo.config.node.approver.ApproverJumpStrategy;
import cn.stylefeng.workflow.api.pojo.config.node.approver.ApproverStrategy;
import cn.stylefeng.workflow.api.pojo.instance.SummaryFieldValue;
import cn.stylefeng.workflow.design.api.ModelApi;
import cn.stylefeng.workflow.instance.api.InstanceApi;
import cn.stylefeng.workflow.instance.api.pojo.TaskApplicantInfo;
import cn.stylefeng.workflow.instance.api.pojo.request.RuntimeInstanceRequest;
import cn.stylefeng.workflow.instance.api.pojo.tag.SimpleRemarkVo;
import cn.stylefeng.workflow.instance.api.pojo.tag.SimpleTagVo;
import cn.stylefeng.workflow.instance.api.pojo.task.CommonTaskItem;
import cn.stylefeng.workflow.instance.core.actions.gateway.ConditionNodeAction;
import cn.stylefeng.workflow.instance.core.condition.holder.SubmitterUserIdHolder;
import cn.stylefeng.workflow.instance.core.path.NodePathCalc;
import cn.stylefeng.workflow.instance.modular.manage.enums.RuntimeInstanceExceptionEnum;
import cn.stylefeng.workflow.instance.modular.manage.factory.DurationFactory;
import cn.stylefeng.workflow.instance.modular.manage.factory.PredictUserItemFactory;
import cn.stylefeng.workflow.instance.modular.manage.factory.TaskSummaryFieldFactory;
import cn.stylefeng.workflow.instance.modular.manage.mapper.RuntimeInstanceMapper;
import cn.stylefeng.workflow.instance.modular.manage.pojo.request.PredictRequest;
import cn.stylefeng.workflow.instance.modular.manage.pojo.request.RuntimeNodeRequest;
import cn.stylefeng.workflow.instance.modular.manage.pojo.vo.PredictResponse;
import cn.stylefeng.workflow.instance.modular.manage.pojo.vo.PredictUserItem;
import cn.stylefeng.workflow.instance.modular.manage.pojo.vo.RuntimeNodeEdgeResult;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeInstanceService;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeNodeEdgeService;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeNodeService;
import cn.stylefeng.workflow.instance.modular.tag.service.RuntimeRemarkService;
import cn.stylefeng.workflow.instance.modular.tag.service.RuntimeTagService;
import cn.stylefeng.workflow.instance.modular.task.service.RuApproverService;
import cn.stylefeng.workflow.instance.modular.task.service.RuTaskService;
import cn.stylefeng.workflow.instance.modular.task.strategy.agree.NodePassStrategyContext;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

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

@Service
public class RuntimeInstanceServiceImpl extends ServiceImpl<RuntimeInstanceMapper, RuntimeInstance> implements RuntimeInstanceService {

    @Resource
    private DefinitionApi definitionApi;

    @Resource
    private ModelApi modelApi;

    @Resource
    private NodeApi nodeApi;

    @Resource
    private RuntimeNodeService runtimeNodeService;

    @Resource
    private RuntimeNodeEdgeService runtimeNodeEdgeService;

    @Resource
    private InstanceApi instanceApi;

    @Resource
    private RuTaskService ruTaskService;

    @Resource
    private RuApproverService ruApproverService;

    @Resource
    private SysUserServiceApi sysUserServiceApi;

    @Resource
    private ConditionNodeAction conditionNodeAction;

    @Resource
    private RuntimeTagService runtimeTagService;

    @Resource
    private RuntimeRemarkService runtimeRemarkService;

    @Override
    public void add(RuntimeInstanceRequest runtimeInstanceRequest) {
        RuntimeInstance runtimeInstance = new RuntimeInstance();
        BeanUtil.copyProperties(runtimeInstanceRequest, runtimeInstance);
        this.save(runtimeInstance);
    }

    @Override
    public void del(RuntimeInstanceRequest runtimeInstanceRequest) {
        RuntimeInstance runtimeInstance = this.queryRuntimeInstance(runtimeInstanceRequest);
        this.removeById(runtimeInstance.getInstanceId());
    }

    @Override
    public void edit(RuntimeInstanceRequest runtimeInstanceRequest) {
        RuntimeInstance runtimeInstance = this.queryRuntimeInstance(runtimeInstanceRequest);
        BeanUtil.copyProperties(runtimeInstanceRequest, runtimeInstance);
        this.updateById(runtimeInstance);
    }

    @Override
    public RuntimeInstance detail(RuntimeInstanceRequest runtimeInstanceRequest) {
        RuntimeInstance runtimeInstance = this.queryRuntimeInstance(runtimeInstanceRequest);

        List<RuntimeNode> currentRuntimeNodeList = this.getCurrentRuntimeNodeList(runtimeInstanceRequest.getInstanceId());
        runtimeInstance.setCurrentRuntimeNodeList(currentRuntimeNodeList);

        return runtimeInstance;
    }

    @Override
    public PageResult<RuntimeInstance> findPage(RuntimeInstanceRequest runtimeInstanceRequest) {
        LambdaQueryWrapper<RuntimeInstance> wrapper = createWrapper(runtimeInstanceRequest);
        Page<RuntimeInstance> runtimeInstancePage = this.page(PageFactory.defaultPage(runtimeInstanceRequest), wrapper);
        return PageResultFactory.createPageResult(runtimeInstancePage);
    }

    @Override
    public List<RuntimeInstance> findList(RuntimeInstanceRequest runtimeInstanceRequest) {
        LambdaQueryWrapper<RuntimeInstance> wrapper = this.createWrapper(runtimeInstanceRequest);
        return this.list(wrapper);
    }

    @Override
    public InstanceContext buildInstanceContext(Long instanceId) {

        RuntimeInstance runtimeInstance = this.instanceApi.getInstanceInfById(instanceId);
        Long definitionId = runtimeInstance.getDefinitionId();

        return this.buildInstanceContext(definitionId, instanceId);
    }

    @Override
    public InstanceContext buildInstanceContext(Long definitionId, Long instanceId) {

        InstanceContext instanceContext = new InstanceContext();

        Definition definitionInfo = this.definitionApi.getDefinitionInfo(definitionId);
        instanceContext.setDefinition(definitionInfo);

        Model modelInfo = this.modelApi.getModelInfo(definitionInfo.getModelId());
        instanceContext.setModel(modelInfo);

        if (instanceId != null) {
            RuntimeInstanceRequest runtimeInstanceRequest = new RuntimeInstanceRequest();
            runtimeInstanceRequest.setInstanceId(instanceId);
            RuntimeInstance detail = this.detail(runtimeInstanceRequest);
            instanceContext.setRuntimeInstance(detail);

            instanceContext.setInstanceVariable(detail.getInstanceVariable());
        }

        List<Node> definitionNodeList = nodeApi.getDefinitionNodeList(definitionId);
        instanceContext.setDefinitionNodeList(definitionNodeList);

        List<NodeEdge> definitionEdgeList = nodeApi.getDefinitionEdgeList(definitionId);
        instanceContext.setDefinitionNodeEdgeList(definitionEdgeList);

        return instanceContext;
    }

    @Override
    public InstanceContext buildInstanceContext(Long definitionId, RuntimeInstance runtimeInstance, InstanceVariable instanceVariable) {
        InstanceContext instanceContext = new InstanceContext();

        Definition definitionInfo = this.definitionApi.getDefinitionInfo(definitionId);
        instanceContext.setDefinition(definitionInfo);

        Model modelInfo = this.modelApi.getModelInfo(definitionInfo.getModelId());
        instanceContext.setModel(modelInfo);

        instanceContext.setRuntimeInstance(runtimeInstance);

        instanceContext.setInstanceVariable(instanceVariable);

        List<Node> definitionNodeList = nodeApi.getDefinitionNodeList(definitionId);
        instanceContext.setDefinitionNodeList(definitionNodeList);

        List<NodeEdge> definitionEdgeList = nodeApi.getDefinitionEdgeList(definitionId);
        instanceContext.setDefinitionNodeEdgeList(definitionEdgeList);

        return instanceContext;
    }

    @Override
    public boolean getInstanceFinishFlag(InstanceContext instanceContext) {


        return false;
    }

    @Override
    public RuntimeNodeEdgeResult getRuntimeNodeEdgeList(Long instanceId) {

        RuntimeNodeRequest runtimeNodeRequest = new RuntimeNodeRequest();
        runtimeNodeRequest.setInstanceId(instanceId);
        List<RuntimeNode> list = this.runtimeNodeService.findList(runtimeNodeRequest);

        List<Long> highLightNodeEdgeList = runtimeNodeEdgeService.getHighLightNodeEdgeList(instanceId);

        RuntimeNodeEdgeResult result = new RuntimeNodeEdgeResult();
        result.setHightLightNodeList(list);
        result.setHightLightEdgeList(highLightNodeEdgeList);

        return result;
    }

    @Override
    public List<PredictResponse> processPredict(PredictRequest predictRequest) {
        Long definitionId = predictRequest.getDefinitionId();
        if (ObjectUtil.isEmpty(definitionId)) {
            throw new ServiceException(RuntimeInstanceExceptionEnum.PREDICT_NULL_ERROR);
        }

        Definition definitionInfo = this.definitionApi.getDefinitionInfo(definitionId);

        List<Node> definitionNodeList = nodeApi.getDefinitionNodeList(definitionId);
        List<NodeEdge> definitionEdgeList = nodeApi.getDefinitionEdgeList(definitionId);
        NodePathCalc nodePathCalc = new NodePathCalc(definitionNodeList, definitionEdgeList);

        if (ObjectUtil.isEmpty(predictRequest.getTaskId())) {
            return this.processPredictByDefinitionId(predictRequest, nodePathCalc, definitionInfo);
        }

        else {
            return this.processPredictByTaskId(predictRequest, nodePathCalc, definitionInfo);
        }
    }

    @Override
    public List<Long> getSubFlowInstanceIdList(Long instanceId) {
        LambdaQueryWrapper<RuntimeInstance> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RuntimeInstance::getParentInstanceId, instanceId);
        wrapper.select(RuntimeInstance::getInstanceId);
        wrapper.orderByAsc(RuntimeInstance::getInstanceId);
        List<RuntimeInstance> list = this.list(wrapper);
        if (ObjectUtil.isEmpty(list)) {
            return new ArrayList<>();
        }

        return list.stream().map(RuntimeInstance::getInstanceId).collect(Collectors.toList());
    }

    @Override
    public RuntimeInstance getSummaryDetail(RuntimeInstanceRequest runtimeInstanceRequest) {
        LambdaQueryWrapper<RuntimeInstance> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RuntimeInstance::getInstanceId, runtimeInstanceRequest.getInstanceId());

        wrapper.select(RuntimeInstance::getInstanceId, RuntimeInstance::getDefinitionId, RuntimeInstance::getInstanceTitle,
                RuntimeInstance::getInstanceStatus, RuntimeInstance::getApplicant, BaseEntity::getCreateTime);
        RuntimeInstance runtimeInstance = this.getOne(wrapper, false);
        if (runtimeInstance == null) {
            return null;
        }

        List<RuntimeNode> currentRuntimeNodeList = this.getCurrentRuntimeNodeList(runtimeInstanceRequest.getInstanceId());
        runtimeInstance.setCurrentRuntimeNodeList(currentRuntimeNodeList);

        String durationTime = DurationFactory.durationTime(runtimeInstance.getCreateTime(), runtimeInstance.getCompletionTime());
        runtimeInstance.setDurationFormat(durationTime);

        return runtimeInstance;
    }

    @Override
    public List<RuntimeNode> getCurrentRuntimeNodeList(Long instanceId) {
        Map<Long, List<RuntimeNode>> currentRuntimeNodeList = this.getCurrentRuntimeNodeList(ListUtil.list(false, instanceId));
        return currentRuntimeNodeList.get(instanceId);
    }

    @Override
    public Map<Long, List<RuntimeNode>> getCurrentRuntimeNodeList(List<Long> instanceIdList) {

        Map<Long, List<RuntimeNode>> resultMap = new HashMap<>();

        RuntimeNodeRequest runtimeNodeRequest = new RuntimeNodeRequest();
        runtimeNodeRequest.setInstanceIdList(instanceIdList);
        runtimeNodeRequest.setNodeStatus(NodeStatusEnum.RUNNING.getStatus());
        List<RuntimeNode> runningNodeList = runtimeNodeService.findList(runtimeNodeRequest);

        if (ObjectUtil.isNotEmpty(runningNodeList)) {

            Map<Long, List<RuntimeNode>> runningNodeMap = runningNodeList.stream().collect(Collectors.groupingBy(RuntimeNode::getInstanceId));
            resultMap.putAll(runningNodeMap);

            return resultMap;
        }

        RuntimeNodeRequest tempRequest = new RuntimeNodeRequest();
        tempRequest.setInstanceIdList(instanceIdList);
        List<RuntimeNode> lastRuntimeNodeList = runtimeNodeService.findList(tempRequest);
        if (ObjectUtil.isNotEmpty(lastRuntimeNodeList)) {

            RuntimeNode runtimeNode = lastRuntimeNodeList.get(0);
            Long instanceId = runtimeNode.getInstanceId();
            resultMap.put(instanceId, ListUtil.list(false, runtimeNode));
            return resultMap;
        }

        return resultMap;
    }

    @Override
    public RuntimeInstance getInstanceInfById(Long instanceId) {
        RuntimeInstance runtimeInstance = this.getById(instanceId);
        if (ObjectUtil.isEmpty(runtimeInstance)) {
            throw new ServiceException(RuntimeInstanceExceptionEnum.RUNTIME_INSTANCE_NOT_EXISTED);
        }
        return runtimeInstance;
    }

    @Override
    public List<Node> calcNextNodeList(Node nodeInfo, InstanceContext instanceContext) {
        return new NodePathCalc(instanceContext).calcNextNodeList(nodeInfo.getNodeId());
    }

    @Override
    public List<CommonTaskItem> findInstanceDetailList(List<CommonTaskItem> originTaskList) {

        List<Long> instanceIdList = originTaskList.stream().map(CommonTaskItem::getInstanceId).collect(Collectors.toList());

        Map<Long, List<RuntimeNode>> currentRuntimeNodeList = this.getCurrentRuntimeNodeList(instanceIdList);

        Map<Long, Set<Long>> currentInstanceApprover = ruTaskService.getInstanceCurrentApprover(instanceIdList);

        Map<Long, List<SummaryFieldValue>> instanceSummaryFieldValueList = instanceApi.getInstanceSummaryFieldValueList(new HashSet<>(instanceIdList));

        Map<Long, List<SimpleTagVo>> userTagListMap = runtimeTagService.findUserTagListMap(instanceIdList);

        Map<Long, List<SimpleRemarkVo>> userRemarkListMap = runtimeRemarkService.findUserRemarkListMap(instanceIdList);

        for (CommonTaskItem record : originTaskList) {

            record.setCurrentRuntimeNodeList(currentRuntimeNodeList.get(record.getInstanceId()));

            record.setCurrentApproverList(currentInstanceApprover.get(record.getInstanceId()));

            record.setSummaryFieldValueList(instanceSummaryFieldValueList.get(record.getInstanceId()));

            String durationTime = DurationFactory.durationTime(record.getCreateTime(), record.getCompletionTime());
            record.setDurationFormat(durationTime);

            record.setTagList(userTagListMap.get(record.getInstanceId()));

            record.setRemarkList(userRemarkListMap.get(record.getInstanceId()));
        }

        return originTaskList;
    }

    @Override
    public List<RuntimeInstance> findInstanceDetailListTotal(Set<Long> instanceIdSet) {
        RuntimeInstanceRequest runtimeInstanceRequest = new RuntimeInstanceRequest();
        runtimeInstanceRequest.setInstanceIdList(instanceIdSet);

        LambdaQueryWrapper<RuntimeInstance> wrapper = createWrapper(runtimeInstanceRequest);
        List<RuntimeInstance> runtimeInstanceList = this.list(wrapper);

        for (RuntimeInstance record : runtimeInstanceList) {

            List<RuntimeNode> currentRuntimeNodeList = this.getCurrentRuntimeNodeList(record.getInstanceId());
            record.setCurrentRuntimeNodeList(currentRuntimeNodeList);

            Set<Long> instanceCurrentApprover = ruTaskService.getInstanceCurrentApprover(record.getInstanceId());
            record.setCurrentApproverList(instanceCurrentApprover);
        }

        return runtimeInstanceList;
    }

    @Override
    public Set<Long> getCurrentUserSubmitInstance() {
        RuntimeInstanceRequest runtimeInstanceRequest = new RuntimeInstanceRequest();

        Long userId = LoginContext.me().getLoginUser().getUserId();
        runtimeInstanceRequest.setApplicant(ListUtil.list(false, userId));

        LambdaQueryWrapper<RuntimeInstance> wrapper = this.createWrapper(runtimeInstanceRequest);
        wrapper.select(RuntimeInstance::getInstanceId);
        List<RuntimeInstance> list = this.list(wrapper);

        return list.stream().map(RuntimeInstance::getInstanceId).collect(Collectors.toSet());
    }

    @Override
    public Map<Long, List<SummaryFieldValue>> getInstanceSummaryFieldValueList(Set<Long> instanceIdList) {

        Map<Long, List<SummaryFieldValue>> resultMap = new HashMap<>();

        RuntimeInstanceRequest runtimeInstanceRequest = new RuntimeInstanceRequest();
        runtimeInstanceRequest.setInstanceIdList(instanceIdList);
        LambdaQueryWrapper<RuntimeInstance> wrapper = createWrapper(runtimeInstanceRequest);
        wrapper.select(RuntimeInstance::getInstanceId, RuntimeInstance::getDefinitionId, RuntimeInstance::getInstanceVariable);
        List<RuntimeInstance> runtimeInstanceList = this.list(wrapper);
        if (ObjectUtil.isEmpty(runtimeInstanceList)) {
            return resultMap;
        }

        Map<Long, Definition> definitionListInfo = definitionApi.getDefinitionListInfo(runtimeInstanceList.stream().map(RuntimeInstance::getDefinitionId).collect(Collectors.toList()));
        if (ObjectUtil.isEmpty(definitionListInfo)) {
            return resultMap;
        }

        for (RuntimeInstance runtimeInstance : runtimeInstanceList) {

            Long instanceId = runtimeInstance.getInstanceId();

            Definition definition = definitionListInfo.get(runtimeInstance.getDefinitionId());

            InstanceVariable instanceVariable = runtimeInstance.getInstanceVariable();

            List<SummaryFieldValue> taskSummaryField = TaskSummaryFieldFactory.createTaskSummaryField(instanceId, definition, instanceVariable);
            resultMap.put(instanceId, taskSummaryField);
        }

        return resultMap;
    }

    @Override
    public void modelRemoveAction(Long modelId) {
        LambdaQueryWrapper<RuntimeInstance> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RuntimeInstance::getModelId, modelId);
        this.remove(queryWrapper);
    }

    private RuntimeInstance queryRuntimeInstance(RuntimeInstanceRequest runtimeInstanceRequest) {
        RuntimeInstance runtimeInstance = this.getById(runtimeInstanceRequest.getInstanceId());
        if (ObjectUtil.isEmpty(runtimeInstance)) {
            throw new ServiceException(RuntimeInstanceExceptionEnum.RUNTIME_INSTANCE_NOT_EXISTED);
        }
        return runtimeInstance;
    }

    private LambdaQueryWrapper<RuntimeInstance> createWrapper(RuntimeInstanceRequest runtimeInstanceRequest) {
        LambdaQueryWrapper<RuntimeInstance> queryWrapper = new LambdaQueryWrapper<>();

        Long instanceId = runtimeInstanceRequest.getInstanceId();
        if (ObjectUtil.isNotEmpty(instanceId)) {
            queryWrapper.eq(RuntimeInstance::getInstanceId, instanceId);
        }

        String instanceTitle = runtimeInstanceRequest.getSearchText();
        queryWrapper.like(ObjectUtil.isNotEmpty(instanceTitle), RuntimeInstance::getInstanceTitle, instanceTitle);

        List<Long> applicant = runtimeInstanceRequest.getApplicant();
        if (ObjectUtil.isNotEmpty(applicant)) {
            queryWrapper.in(RuntimeInstance::getApplicant, applicant);
        }

        Long applicantOrgId = runtimeInstanceRequest.getApplicantOrgId();
        queryWrapper.eq(ObjectUtil.isNotNull(applicantOrgId), RuntimeInstance::getApplicantOrgId, applicantOrgId);

        List<Integer> instanceStatusList = runtimeInstanceRequest.getInstanceStatusList();
        queryWrapper.in(ObjectUtil.isNotEmpty(instanceStatusList), RuntimeInstance::getInstanceStatus, instanceStatusList);

        List<Long> modelTreeIdList = runtimeInstanceRequest.getModelTreeIdList();
        if (ObjectUtil.isNotEmpty(modelTreeIdList)) {
            queryWrapper.in(RuntimeInstance::getModelId, modelTreeIdList);
        }

        Set<Long> instanceIdList = runtimeInstanceRequest.getInstanceIdList();
        if (ObjectUtil.isNotEmpty(instanceIdList)) {
            queryWrapper.in(RuntimeInstance::getInstanceId, instanceIdList);
        }

        String searchBeginTime = runtimeInstanceRequest.getSearchBeginTime();
        if (ObjectUtil.isNotEmpty(searchBeginTime)) {

            String beginTime = searchBeginTime + " 00:00:00";
            String endTime = searchBeginTime + " 23:59:59";
            queryWrapper.between(RuntimeInstance::getCreateTime, beginTime, endTime);
        }

        queryWrapper.orderByDesc(RuntimeInstance::getInstanceId);

        return queryWrapper;
    }

    private List<PredictResponse> processPredictByDefinitionId(PredictRequest predictRequest, NodePathCalc nodePathCalc, Definition definitionInfo) {

        Long applicant = LoginContext.me().getLoginUser().getUserId();

        Node applicantNode = nodePathCalc.getPointNode(NodeTypeEnum.APPLICANT);

        List<Node> nextNodeList = nodePathCalc.calcNextNodeList(applicantNode.getNodeId());

        if (ObjectUtil.isEmpty(nextNodeList)) {
            return null;
        }

        Long currentOrgId = LoginContext.me().getLoginUser().getCurrentOrgId();

        try {
            SubmitterUserIdHolder.setSubmitter(applicant);
            return commonPredictPointNodeNext(predictRequest, currentOrgId, definitionInfo, applicant, nodePathCalc, nextNodeList);
        } finally {
            SubmitterUserIdHolder.clearParam();
        }
    }

    private List<PredictResponse> processPredictByTaskId(PredictRequest predictRequest, NodePathCalc nodePathCalc, Definition definitionInfo) {

        Long taskId = predictRequest.getTaskId();

        TaskApplicantInfo taskApplicantInfo = ruTaskService.getTaskApplicantInfo(taskId);

        if (taskApplicantInfo.getTaskReNode() == null) {
            return null;
        }


        boolean ifFinishCurrentNode = NodePassStrategyContext.getAgreeStrategy(taskApplicantInfo.getTask(), nodeApi, true);
        if (!ifFinishCurrentNode) {
            return null;
        }

        List<Node> nextNodeList = nodePathCalc.calcNextNodeList(taskApplicantInfo.getTaskReNode().getNodeId());

        if (ObjectUtil.isEmpty(nextNodeList)) {
            return null;
        }

        RuTask ruTask = ruTaskService.getById(taskId);
        if (ruTask == null) {
            return null;
        }

        Long instanceId = ruTask.getInstanceId();
        if (instanceId == null) {
            return null;
        }

        RuntimeInstance runtimeInstance = this.getInstanceInfById(instanceId);
        if (runtimeInstance == null) {
            return null;
        }

        try {
            SubmitterUserIdHolder.setSubmitter(runtimeInstance.getApplicant());
            return commonPredictPointNodeNext(predictRequest, runtimeInstance.getApplicantOrgId(), definitionInfo, taskApplicantInfo.getApplicant(), nodePathCalc, nextNodeList);
        } finally {
            SubmitterUserIdHolder.clearParam();
        }
    }

    private ArrayList<PredictResponse> commonPredictPointNodeNext(PredictRequest predictRequest,
                                                                  Long submitterUserOrgId,
                                                                  Definition definitionInfo,
                                                                  Long applicantId,
                                                                  NodePathCalc nodePathCalc,
                                                                  List<Node> nextNodeList) {
        ArrayList<PredictResponse> predictResponses = new ArrayList<>();
        for (Node node : nextNodeList) {

            if (NodeTypeEnum.CONDITION_GATEWAY_CONDITION.getNodeType().equals(node.getNodeType())) {

                NodeData nodeData = node.getNodeData();
                String expressionString = nodeData.getNodeCondition().getExpressionString();

                boolean thisConditionRight = conditionNodeAction.commonConditionRuleResult(expressionString, applicantId, predictRequest.getInstanceVariable());
                if (thisConditionRight) {
                    List<Node> tempNodeList = nodePathCalc.calcNextNodeList(node.getNodeId());
                    predictResponses.addAll(commonPredictPointNodeNext(predictRequest, submitterUserOrgId, definitionInfo, applicantId, nodePathCalc, tempNodeList));
                    continue;
                }
            }

            else if (!NodeTypeEnum.USER_APPROVAL.getNodeType().equals(node.getNodeType()) && !NodeTypeEnum.HANDLE.getNodeType().equals(node.getNodeType())) {
                List<Node> tempNodeList = nodePathCalc.calcNextNodeList(node.getNodeId());
                predictResponses.addAll(commonPredictPointNodeNext(predictRequest, submitterUserOrgId, definitionInfo, applicantId, nodePathCalc, tempNodeList));
                continue;
            }

            PredictResponse predictResponse = new PredictResponse();

            predictResponse.setNextNodeId(node.getNodeId());

            predictResponse.setNextNodeName(node.getNodeName());

            NodeData nodeData = node.getNodeData();
            if (ObjectUtil.isEmpty(nodeData)) {
                continue;
            }

            ApproverConfig approverConfig = nodeData.getApproverConfig();
            if (ObjectUtil.isEmpty(approverConfig)) {
                continue;
            }

            ApproverStrategy approverStrategy = approverConfig.getApproverStrategy();
            if (ObjectUtil.isEmpty(approverStrategy)) {
                continue;
            }

            Integer approverNodeType = approverStrategy.getApproverNodeType();
            if (ObjectUtil.isEmpty(approverNodeType)) {
                continue;
            }

            predictResponse.setApproverNodeType(approverNodeType);

            Set<Long> userIdList = ruApproverService.customGetNodeApproverResult(approverConfig, submitterUserOrgId, definitionInfo, predictRequest.getInstanceVariable());

            List<SimpleUserDTO> simpleUserDTOS = this.sysUserServiceApi.batchGetUserSimpleInfoList(userIdList);

            List<PredictUserItem> predictUserItemList = PredictUserItemFactory.createPredictUserItemList(simpleUserDTOS);

            predictResponse.setApproverPersons(predictUserItemList);

            ApproverJumpStrategy approverJumpStrategy = approverConfig.getApproverJumpStrategy();
            if (ObjectUtil.isNotEmpty(approverJumpStrategy)) {
                predictResponse.setAllowSelfSelect(approverJumpStrategy.getAllowSelfSelect());
            }

            predictResponses.add(predictResponse);
        }

        return predictResponses;
    }

}