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

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
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.enums.YesOrNotEnum;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.team.form.api.enums.FormTypeEnum;
import cn.stylefeng.team.project.api.FormDataServiceApi;
import cn.stylefeng.team.project.api.pojo.BusinessRenderDetailVo;
import cn.stylefeng.team.project.api.pojo.request.BusinessDataRequest;
import cn.stylefeng.workflow.api.DefinitionApi;
import cn.stylefeng.workflow.api.NodeApi;
import cn.stylefeng.workflow.api.entity.design.Node;
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.*;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import cn.stylefeng.workflow.api.pojo.InstanceVariable;
import cn.stylefeng.workflow.api.pojo.config.model.ModelForm;
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.ApproverStrategy;
import cn.stylefeng.workflow.api.pojo.config.node.button.NodeButtonPermission;
import cn.stylefeng.workflow.api.pojo.config.node.notice.NodeNotice;
import cn.stylefeng.workflow.api.util.WorkFlowStatusUtil;
import cn.stylefeng.workflow.event.api.EventPublishApi;
import cn.stylefeng.workflow.instance.api.InstanceApi;
import cn.stylefeng.workflow.instance.api.RuntimeNodeApi;
import cn.stylefeng.workflow.instance.api.WorkflowControlApi;
import cn.stylefeng.workflow.instance.api.pojo.TaskApplicantInfo;
import cn.stylefeng.workflow.instance.api.pojo.TaskQueryParam;
import cn.stylefeng.workflow.instance.api.pojo.request.RuntimeInstanceRequest;
import cn.stylefeng.workflow.instance.api.pojo.request.WorkflowFinishNodeRequest;
import cn.stylefeng.workflow.instance.api.pojo.task.CommonTaskItem;
import cn.stylefeng.workflow.instance.api.pojo.task.RuTaskRequest;
import cn.stylefeng.workflow.instance.modular.message.TaskMessageSendFactory;
import cn.stylefeng.workflow.instance.modular.task.enums.RuTaskExceptionEnum;
import cn.stylefeng.workflow.instance.modular.task.factory.RuTaskFactory;
import cn.stylefeng.workflow.instance.modular.task.factory.TaskButtonStrategyFactory;
import cn.stylefeng.workflow.instance.modular.task.factory.TaskNumberFactory;
import cn.stylefeng.workflow.instance.modular.task.factory.TaskQueryConditionFactory;
import cn.stylefeng.workflow.instance.modular.task.mapper.RuTaskMapper;
import cn.stylefeng.workflow.instance.modular.task.pojo.NodeTaskCountVo;
import cn.stylefeng.workflow.instance.modular.task.pojo.TaskDetailVo;
import cn.stylefeng.workflow.instance.modular.task.pojo.ToDoNumberVo;
import cn.stylefeng.workflow.instance.modular.task.pojo.TotalTaskNumberVo;
import cn.stylefeng.workflow.instance.modular.task.pojo.request.TaskButtonPermissionRequest;
import cn.stylefeng.workflow.instance.modular.task.pojo.request.TaskFormDataRequest;
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.service.TaskDaoWithEvent;
import cn.stylefeng.workflow.instance.modular.task.strategy.PreTaskProcessFactory;
import cn.stylefeng.workflow.log.api.WorkflowLogApi;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@Slf4j
public class RuTaskServiceImpl extends ServiceImpl<RuTaskMapper, RuTask> implements RuTaskService {

    @Resource
    private RuApproverService ruApproverService;

    @Resource
    private EventPublishApi eventPublishApi;

    @Resource
    private WorkflowControlApi workflowControlApi;

    @Resource
    private InstanceApi instanceApi;

    @Resource
    private RuntimeNodeApi runtimeNodeApi;

    @Resource
    private NodeApi nodeApi;

    @Resource
    private FormDataServiceApi formDataServiceApi;

    @Resource
    private DefinitionApi definitionApi;

    @Resource
    private WorkflowLogApi workflowLogApi;

    @Resource
    private TaskDaoWithEvent taskDaoWithEvent;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createNodeTask(Node node, TaskTypeEnum taskTypeEnum, RuntimeNode runtimeNode, InstanceContext instanceContext) {

        ApproverConfig approverConfig = node.getNodeData().getApproverConfig();

        Set<Long> nodeApproverUserIdList = null;

        InstanceVariable instanceVariable = instanceContext.getInstanceVariable();
        if (instanceVariable != null) {
            Map<Long, List<Long>> pointNodeApproverUserId = instanceVariable.getPointNodeApproverUserId();
            if (ObjectUtil.isNotEmpty(pointNodeApproverUserId)) {
                List<Long> pointUserIdList = pointNodeApproverUserId.get(node.getNodeId());
                if (ObjectUtil.isNotEmpty(pointUserIdList)) {
                    nodeApproverUserIdList = new LinkedHashSet<>(pointUserIdList);
                }
            }
        }

        if (nodeApproverUserIdList != null && ApproverNodeTypeEnum.ONE_BY_ONE.getMode().equals(approverConfig.getApproverStrategy().getApproverNodeType())) {
            nodeApproverUserIdList = CollectionUtil.set(true, nodeApproverUserIdList.iterator().next());
        }

        if (ObjectUtil.isEmpty(nodeApproverUserIdList)) {
            nodeApproverUserIdList = this.ruApproverService.getNodeApproverResult(approverConfig, instanceContext);
        }

        if (ObjectUtil.isEmpty(nodeApproverUserIdList)) {
            PreTaskProcessFactory.processEmptyApproverStrategy(node, approverConfig, instanceContext, runtimeNode);
            return;
        }

        if (nodeApproverUserIdList.size() > 100) {
            workflowLogApi.logFail(instanceContext, node.getNodeId(), "审批人数超过100，请检查是否有配置错误，为安全起见，不创建本节点的待办任务！");
            return;
        }

        ArrayList<RuTask> createWaitDoTaskList = new ArrayList<>();
        for (Long userId : nodeApproverUserIdList) {
            RuTask processingTask = RuTaskFactory.createBaseApproverTask(userId, taskTypeEnum, runtimeNode, instanceContext);
            createWaitDoTaskList.add(processingTask);
        }

        List<RuTask> processedFinalTaskList = PreTaskProcessFactory.processSameNeighbor(node, createWaitDoTaskList, approverConfig, instanceContext);

        taskDaoWithEvent.batchSaveTaskList(processedFinalTaskList, instanceContext);
        TaskMessageSendFactory.taskMessageSend(processedFinalTaskList, node.getNodeData().getNodeNotice(), instanceContext);

        boolean haveRunningFlag = WorkFlowStatusUtil.haveRunningTaskOrHandling(processedFinalTaskList);
        if (!haveRunningFlag) {

            eventPublishApi.publishNodeEvent(NodeEventTypeEnum.APPROVAL_PASSED, node.getNodeId(), instanceContext);

            WorkflowFinishNodeRequest workflowFinishNodeRequest = new WorkflowFinishNodeRequest(instanceContext.getRuntimeInstance().getInstanceId(), ListUtil.toList(runtimeNode.getRuNodeId()));
            workflowControlApi.finishNode(workflowFinishNodeRequest);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createWaitToDoTask(Node node, RuntimeNode runtimeNode, InstanceContext instanceContext) {
        this.createNodeTask(node, TaskTypeEnum.PROCESSING, runtimeNode, instanceContext);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createWaitToReadTask(Node node, RuntimeNode runtimeNode, InstanceContext instanceContext) {
        this.createNodeTask(node, TaskTypeEnum.COPY, runtimeNode, instanceContext);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createWaitToHandleTask(Node node, RuntimeNode runtimeNode, InstanceContext instanceContext) {
        this.createNodeTask(node, TaskTypeEnum.HANDLE, runtimeNode, instanceContext);
    }

    @Override
    public void createUserTask(Long approverId, TaskTypeEnum taskTypeEnum, RuntimeNode runtimeNode, InstanceContext instanceContext) {

        if (TaskTypeEnum.COPY.equals(taskTypeEnum)) {
            boolean alreadyExisted = this.validateCopyTaskAlreadyExisted(approverId, runtimeNode.getInstanceId());
            if (alreadyExisted) {
                return;
            }
        }

        RuTask processingTask = RuTaskFactory.createBaseApproverTask(approverId, taskTypeEnum, runtimeNode, instanceContext);

        Node nodeInfo = nodeApi.getNodeInfo(runtimeNode.getNodeId());
        List<RuTask> processedFinalTaskList = PreTaskProcessFactory.processSameNeighbor(
                nodeInfo, ListUtil.toList(processingTask), nodeInfo.getNodeData().getApproverConfig(), instanceContext);
        this.taskDaoWithEvent.batchSaveTaskList(processedFinalTaskList, instanceContext);

        NodeNotice nodeNotice = nodeInfo.getNodeData().getNodeNotice();

        if (nodeNotice == null) {
            return;
        }

        TaskMessageSendFactory.taskMessageSend(ListUtil.of(processingTask), nodeNotice, instanceContext);
    }

    @Override
    public RuTask detail(RuTaskRequest ruTaskRequest) {
        return this.queryRuTask(ruTaskRequest);
    }

    @Override
    public ToDoNumberVo getUserWaitToTaskNum(RuTaskRequest ruTaskRequest) {

        LoginUser loginUser = LoginContext.me().getLoginUser();
        Long userId = loginUser.getUserId();
        ruTaskRequest.setApprover(userId);

        LambdaQueryWrapper<RuTask> wrapper = this.createWrapper(ruTaskRequest);
        wrapper.eq(RuTask::getTaskType, TaskTypeEnum.PROCESSING.getCode());
        wrapper.eq(RuTask::getTaskStatus, TaskStatusEnum.PROCESSING.getKey());
        long toDoCount = this.count(wrapper);

        LambdaQueryWrapper<RuTask> toReadWrapper = this.createWrapper(ruTaskRequest);
        toReadWrapper.eq(RuTask::getTaskType, TaskTypeEnum.COPY.getCode());
        toReadWrapper.eq(RuTask::getReadFlag, YesOrNotEnum.N.getCode());
        long toReadCount = this.count(toReadWrapper);

        ToDoNumberVo toDoNumberVo = new ToDoNumberVo();
        toDoNumberVo.setToDoTaskNumber(Convert.toInt(toDoCount));
        toDoNumberVo.setToReadTaskNumber(Convert.toInt(toReadCount));
        return toDoNumberVo;
    }

    @Override
    public List<RuTask> findList(RuTaskRequest ruTaskRequest) {
        LambdaQueryWrapper<RuTask> wrapper = this.createWrapper(ruTaskRequest);
        return this.list(wrapper);
    }

    @Override
    public PageResult<CommonTaskItem> getUserTasks(RuntimeInstanceRequest runtimeInstanceRequest) {

        TaskQueryParam taskQueryParam = TaskQueryConditionFactory.createTaskQueryParam(runtimeInstanceRequest);

        Page<CommonTaskItem> commonTaskItemPage = PageFactory.defaultPage(runtimeInstanceRequest);

        List<CommonTaskItem> userTaskList;
        if (runtimeInstanceRequest.getTaskCondition().equals(TaskConditionEnum.MY_SUBMIT)) {
            userTaskList = this.getBaseMapper().findMySubmitTaskPage(commonTaskItemPage, taskQueryParam);
        } else {
            userTaskList = this.getBaseMapper().findUserTaskPage(commonTaskItemPage, taskQueryParam);
        }

        if (ObjectUtil.isEmpty(userTaskList)) {
            return PageResultFactory.createPageResult(commonTaskItemPage);
        }

        List<CommonTaskItem> instanceDetailList = instanceApi.findInstanceDetailList(userTaskList);
        commonTaskItemPage.setRecords(instanceDetailList);
        return PageResultFactory.createPageResult(commonTaskItemPage);
    }

    @Override
    public List<CommonTaskItem> getUserTasksTotal(RuntimeInstanceRequest runtimeInstanceRequest) {

        TaskQueryParam taskQueryParam = TaskQueryConditionFactory.createTaskQueryParam(runtimeInstanceRequest);


        List<CommonTaskItem> userTaskList;
        if (runtimeInstanceRequest.getTaskCondition().equals(TaskConditionEnum.MY_SUBMIT)) {
            userTaskList = this.getBaseMapper().findMySubmitTaskTotalList(taskQueryParam);
        } else {
            userTaskList = this.getBaseMapper().findUserTaskTotalList(taskQueryParam);
        }

        if (ObjectUtil.isEmpty(userTaskList)) {
            return new ArrayList<>();
        }

        return instanceApi.findInstanceDetailList(userTaskList);
    }

    @Override
    public Set<Long> getUserHandledInstanceIdList(List<Long> userIdList) {
        if (userIdList == null || userIdList.isEmpty()) {
            return new HashSet<>();
        }
        LambdaQueryWrapper<RuTask> ruTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ruTaskLambdaQueryWrapper.in(RuTask::getApprover, userIdList);
        ruTaskLambdaQueryWrapper.in(RuTask::getTaskStatus, TaskStatusEnum.START.getCode(), TaskStatusEnum.PROCESSING.getCode(), TaskStatusEnum.PROCESSED.getCode());
        ruTaskLambdaQueryWrapper.select(RuTask::getInstanceId);
        List<RuTask> list = this.list(ruTaskLambdaQueryWrapper);
        if (ObjectUtil.isEmpty(list)) {
            return new HashSet<>();
        }
        return list.stream().map(RuTask::getInstanceId).collect(Collectors.toSet());
    }

    @Override
    public Set<Long> getInstanceIdsInStatus(List<Long> instanceIds, List<Integer> taskStatus) {

        if(ObjectUtil.isEmpty(instanceIds) && ObjectUtil.isEmpty(taskStatus)){
            return new HashSet<>();
        }

        LambdaQueryWrapper<RuTask> ruTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ruTaskLambdaQueryWrapper.in(RuTask::getInstanceId, instanceIds);
        ruTaskLambdaQueryWrapper.in(RuTask::getTaskStatus, taskStatus);
        ruTaskLambdaQueryWrapper.select(RuTask::getInstanceId);
        List<RuTask> list = this.list(ruTaskLambdaQueryWrapper);
        if (ObjectUtil.isEmpty(list)) {
            return new HashSet<>();
        }

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

    @Override
    public List<RuTask> getUserProcessingAndHandlingTaskList(Long userId, Long instanceId) {
        LambdaQueryWrapper<RuTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RuTask::getApprover, userId);
        if (ObjectUtil.isNotEmpty(instanceId)) {
            queryWrapper.eq(RuTask::getInstanceId, instanceId);
        }
        queryWrapper.in(RuTask::getTaskType, TaskTypeEnum.PROCESSING.getCode(), TaskTypeEnum.HANDLE.getCode());
        queryWrapper.in(RuTask::getTaskStatus, TaskStatusEnum.PROCESSING.getCode(), TaskStatusEnum.WAIT_TO_START.getCode());
        return this.list(queryWrapper);
    }

    @Override
    public void deleteAnyOtherTask(Long ruNodeId, Long taskId) {

        LambdaQueryWrapper<RuTask> ruTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ruTaskLambdaQueryWrapper.ne(RuTask::getTaskId, taskId);
        ruTaskLambdaQueryWrapper.eq(RuTask::getRuNodeId, ruNodeId);
        ruTaskLambdaQueryWrapper.eq(RuTask::getTaskStatus, TaskStatusEnum.PROCESSING.getCode());
        List<RuTask> list = this.list(ruTaskLambdaQueryWrapper);
        if (ObjectUtil.isEmpty(list)) {
            return;
        }

        this.taskDaoWithEvent.removeTaskList(list);
    }

    @Override
    public void deleteAnyOtherTask(boolean canDelete, Long ruNodeId, Long taskId) {
        if (canDelete) {
            this.deleteAnyOtherTask(ruNodeId, taskId);
        }
    }

    @Override
    public void deleteInstanceProcessingTask(Long instanceId) {

        LambdaQueryWrapper<RuTask> ruTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ruTaskLambdaQueryWrapper.eq(RuTask::getInstanceId, instanceId);
        ruTaskLambdaQueryWrapper.eq(RuTask::getTaskStatus, TaskStatusEnum.PROCESSING.getCode());
        List<RuTask> list = this.list(ruTaskLambdaQueryWrapper);
        if (ObjectUtil.isEmpty(list)) {
            return;
        }

        this.taskDaoWithEvent.removeTaskList(list);
    }

    @Override
    public List<RuntimeNode> getBackReNodeList(RuTaskRequest ruTaskRequest) {

        LambdaQueryWrapper<RuTask> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(RuTask::getTaskId, ruTaskRequest.getTaskId());

        queryWrapper.select(RuTask::getRuNodeId, RuTask::getInstanceId);

        RuTask ruTask = this.getOne(queryWrapper);

        if (ruTask == null) {
            return new ArrayList<>();
        }

        List<RuntimeNode> alreadyFinishedRuntimeNode = runtimeNodeApi.getAlreadyFinishedRuntimeNode(ruTask.getInstanceId(), ruTask.getRuNodeId());
        if (ObjectUtil.isEmpty(alreadyFinishedRuntimeNode)) {
            return alreadyFinishedRuntimeNode;
        }

        alreadyFinishedRuntimeNode.removeIf(runtimeNode -> NodeTypeEnum.START.getNodeType().equals(runtimeNode.getNodeType())
                || NodeTypeEnum.APPLICANT.getNodeType().equals(runtimeNode.getNodeType()));

        for (RuntimeNode runtimeNode : alreadyFinishedRuntimeNode) {
            runtimeNode.setRuNodeId(null);
        }

        return alreadyFinishedRuntimeNode;
    }

    @Override
    public NodeButtonPermission getTaskButtonPermission(TaskButtonPermissionRequest taskButtonPermissionRequest) {

        RuTask ruTask = null;

        if (ObjectUtil.isNotEmpty(taskButtonPermissionRequest.getTaskId())) {
            ruTask = this.getById(taskButtonPermissionRequest.getTaskId());
        }

        return TaskButtonStrategyFactory.getNodeButtonPermission(taskButtonPermissionRequest.getTaskCondition(), ruTask, taskButtonPermissionRequest.getInstanceId());
    }

    @Override
    public BusinessRenderDetailVo getTaskFormData(TaskFormDataRequest taskFormDataRequest) {

        Long instanceId = taskFormDataRequest.getInstanceId();
        RuntimeInstance runtimeInstance = instanceApi.getInstanceInfById(instanceId);

        String bizPrimaryKey = runtimeInstance.getBizPrimaryKey();

        Long definitionId = runtimeInstance.getDefinitionId();
        List<ModelForm> modelFormList = this.definitionApi.getDefinitionFormDefinitionIdList(definitionId);
        if (ObjectUtil.isEmpty(modelFormList)) {
            throw new ServiceException(RuTaskExceptionEnum.CANT_FIND_FORM_LIST);
        }

        Integer formType = modelFormList.get(0).getFormType();

        if (FormTypeEnum.APP_DESIGN.getValueType().equals(formType)) {
            BusinessDataRequest businessDataRequest = new BusinessDataRequest();
            businessDataRequest.setPrimaryKeyId(bizPrimaryKey);
            List<Long> formDefinitionIdList = modelFormList.stream().map(ModelForm::getFormDefinitionId).collect(Collectors.toList());
            businessDataRequest.setFormDefinitionIdList(formDefinitionIdList);
            return formDataServiceApi.getBusinessRenderDetail(businessDataRequest);
        }

        else {
            BusinessRenderDetailVo businessRenderDetailVo = new BusinessRenderDetailVo();
            businessRenderDetailVo.setPrimaryKeyId(bizPrimaryKey);
            businessRenderDetailVo.setCustomVueFormData(runtimeInstance.getInstanceVariable().getCustomVueFormData());
            return businessRenderDetailVo;
        }
    }

    @Override
    public NodeTaskCountVo getNodeTaskCount(Long ruNodeId) {
        if (ObjectUtil.isEmpty(ruNodeId)) {
            return null;
        }

        LambdaQueryWrapper<RuTask> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(RuTask::getRuNodeId, ruNodeId);
        lambdaQueryWrapper.in(RuTask::getTaskType, TaskTypeEnum.PROCESSING.getCode(), TaskTypeEnum.HANDLE.getCode());
        lambdaQueryWrapper.in(RuTask::getTaskStatus, ListUtil.toList(TaskStatusEnum.WAIT_TO_START.getCode(), TaskStatusEnum.PROCESSING.getCode(), TaskStatusEnum.PROCESSED.getCode()));
        lambdaQueryWrapper.select(RuTask::getTaskId, RuTask::getTaskStatus, RuTask::getApprover);
        List<RuTask> totalTask = this.list(lambdaQueryWrapper);
        if (ObjectUtil.isEmpty(totalTask)) {
            return null;
        }

        List<RuTask> finishedTask = totalTask.stream().filter(ruTask -> TaskStatusEnum.PROCESSED.getCode().equals(ruTask.getTaskStatus())).collect(Collectors.toList());

        Set<Long> finishedApprover = finishedTask.stream().map(RuTask::getApprover).collect(Collectors.toSet());

        NodeTaskCountVo nodeTaskCountVo = new NodeTaskCountVo();
        nodeTaskCountVo.setNodeTaskCount(totalTask.size());
        nodeTaskCountVo.setAlreadyAgreeCount(finishedTask.size());
        nodeTaskCountVo.setAlreadyAgreeUserIds(finishedApprover);
        return nodeTaskCountVo;
    }

    @Override
    public Integer getTaskNodeApproverNodeType(RuTaskRequest ruTaskRequest) {

        RuTask detail = this.detail(ruTaskRequest);

        Node nodeInfo = nodeApi.getNodeInfo(detail.getReNodeId());

        if (ObjectUtil.isEmpty(nodeInfo)) {
            return ApproverNodeTypeEnum.SINGLE.getMode();
        }

        NodeData nodeData = nodeInfo.getNodeData();
        if (ObjectUtil.isEmpty(nodeData)) {
            return ApproverNodeTypeEnum.SINGLE.getMode();
        }

        ApproverConfig approverConfig = nodeData.getApproverConfig();
        if (ObjectUtil.isEmpty(approverConfig)) {
            return ApproverNodeTypeEnum.SINGLE.getMode();
        }

        ApproverStrategy approverStrategy = approverConfig.getApproverStrategy();
        if (ObjectUtil.isEmpty(approverStrategy)) {
            return ApproverNodeTypeEnum.SINGLE.getMode();
        }

        return approverStrategy.getApproverNodeType();
    }

    @Override
    public List<RuTask> findListByRuNodeIds(List<Long> ruNodeIdList, TaskStatusEnum taskStatusEnum) {
        if (ObjectUtil.isEmpty(ruNodeIdList)) {
            return new ArrayList<>();
        }

        LambdaQueryWrapper<RuTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(RuTask::getRuNodeId, ruNodeIdList);
        queryWrapper.eq(RuTask::getTaskStatus, taskStatusEnum.getCode());
        return this.list(queryWrapper);
    }

    @Override
    public TotalTaskNumberVo getUserTotalTaskNum(RuTaskRequest ruTaskRequest) {

        LoginUser loginUser = LoginContext.me().getLoginUser();
        Long userId = loginUser.getUserId();
        ruTaskRequest.setApprover(userId);

        LambdaQueryWrapper<RuTask> wrapper = this.createWrapper(ruTaskRequest);
        wrapper.in(RuTask::getTaskType, ListUtil.toList(TaskTypeEnum.START.getCode(), TaskTypeEnum.PROCESSING.getCode(), TaskTypeEnum.COPY.getCode()));
        wrapper.select(RuTask::getTaskStatus, RuTask::getTaskType, RuTask::getReadFlag);
        List<RuTask> totalTaskSummary = this.list(wrapper);

        Set<Long> currentUserSubmitInstance = instanceApi.getCurrentUserSubmitInstance();

        return TaskNumberFactory.createTotalTaskNumber(totalTaskSummary, currentUserSubmitInstance);
    }

    @Override
    public TaskDetailVo getTaskDetail(RuTaskRequest ruTaskRequest) {

        RuTask taskItem = null;
        if (ObjectUtil.isNotEmpty(ruTaskRequest.getTaskId())) {
            LambdaQueryWrapper<RuTask> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(RuTask::getTaskId, ruTaskRequest.getTaskId());
            lambdaQueryWrapper.select(RuTask::getTaskId, RuTask::getTaskId, BaseEntity::getCreateTime, RuTask::getTaskStatus, RuTask::getInstanceId);
            taskItem = this.getOne(lambdaQueryWrapper, false);
        }

        Long instanceId = null;
        if (taskItem != null) {
            instanceId = taskItem.getInstanceId();
        } else {
            instanceId = ruTaskRequest.getInstanceId();
        }
        RuntimeInstance runtimeInstance = null;
        if (instanceId != null) {
            runtimeInstance = instanceApi.getInstanceInfById(instanceId);
        }

        return RuTaskFactory.createTaskDetail(taskItem, runtimeInstance);
    }

    @Override
    public List<RuTask> getInstanceIdRunningTask(Long instanceId) {
        LambdaQueryWrapper<RuTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RuTask::getInstanceId, instanceId);
        queryWrapper.in(RuTask::getTaskType, ListUtil.toList(TaskTypeEnum.PROCESSING.getCode(), TaskTypeEnum.COPY.getCode()));
        queryWrapper.nested(i ->
                i.eq(RuTask::getTaskStatus, TaskStatusEnum.PROCESSING.getCode())
                        .or()
                        .eq(RuTask::getReadFlag, YesOrNotEnum.N.getCode()));
        return this.list(queryWrapper);
    }

    @Override
    public Set<Long> getInstanceCurrentApprover(Long instanceId) {
        Map<Long, Set<Long>> instanceCurrentApprover = this.getInstanceCurrentApprover(ListUtil.toList(instanceId));
        return instanceCurrentApprover.get(instanceId);
    }

    @Override
    public Map<Long, Set<Long>> getInstanceCurrentApprover(List<Long> instanceIdList) {
        RuTaskRequest ruTaskRequest = new RuTaskRequest();

        ruTaskRequest.setInstanceIdList(instanceIdList);
        ruTaskRequest.setTaskType(TaskTypeEnum.PROCESSING.getCode());
        ruTaskRequest.setTaskStatus(TaskStatusEnum.PROCESSING.getCode());
        LambdaQueryWrapper<RuTask> wrapper = this.createWrapper(ruTaskRequest);

        wrapper.select(RuTask::getInstanceId, RuTask::getApprover);

        List<RuTask> list = this.list(wrapper);

        if (ObjectUtil.isEmpty(list)) {
            return new HashMap<>();
        }

        Map<Long, Set<Long>> instanceIdApproverMap = new HashMap<>();
        for (RuTask ruTask : list) {
            Long instanceId = ruTask.getInstanceId();
            Long approver = ruTask.getApprover();
            Set<Long> approverSet = instanceIdApproverMap.get(instanceId);
            if (ObjectUtil.isEmpty(approverSet)) {
                approverSet = new HashSet<>();
                instanceIdApproverMap.put(instanceId, approverSet);
            }
            approverSet.add(approver);
        }
        return instanceIdApproverMap;
    }

    @Override
    public Node getTaskNode(Long taskId) {

        RuTask ruTask = this.getById(taskId);

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

        Long reNodeId = ruTask.getReNodeId();
        if (ObjectUtil.isEmpty(reNodeId)) {
            return null;
        }

        return this.nodeApi.getNodeInfo(reNodeId);
    }

    @Override
    public Integer getTaskStatus(Long taskId) {
        LambdaQueryWrapper<RuTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RuTask::getTaskId, taskId);
        queryWrapper.select(RuTask::getTaskStatus);
        RuTask taskInfo = this.getOne(queryWrapper, false);
        if (ObjectUtil.isEmpty(taskInfo)) {
            return null;
        }
        return taskInfo.getTaskStatus();
    }

    @Override
    public TaskApplicantInfo getTaskApplicantInfo(Long taskId) {
        RuTask taskInfo = this.getById(taskId);
        if (ObjectUtil.isEmpty(taskInfo)) {
            return null;
        }

        TaskApplicantInfo taskApplicantInfo = new TaskApplicantInfo();
        taskApplicantInfo.setTask(taskInfo);

        RuntimeInstance runtimeInstance = instanceApi.getInstanceInfById(taskInfo.getInstanceId());
        Long applicant = runtimeInstance.getApplicant();
        taskApplicantInfo.setApplicant(applicant);

        Long reNodeId = taskInfo.getReNodeId();
        if (ObjectUtil.isEmpty(reNodeId)) {
            return taskApplicantInfo;
        }
        Node nodeInfo = this.nodeApi.getNodeInfo(reNodeId);
        taskApplicantInfo.setTaskReNode(nodeInfo);

        return taskApplicantInfo;
    }

    @Override
    public void modelRemoveAction(Long modelId) {

        LambdaQueryWrapper<RuTask> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RuTask::getModelId, modelId);
        this.remove(wrapper);
    }

    private RuTask queryRuTask(RuTaskRequest ruTaskRequest) {
        RuTask ruTask = this.getById(ruTaskRequest.getTaskId());
        if (ObjectUtil.isEmpty(ruTask)) {
            throw new ServiceException(RuTaskExceptionEnum.RU_TASK_NOT_EXISTED);
        }
        return ruTask;
    }

    private LambdaQueryWrapper<RuTask> createWrapper(RuTaskRequest ruTaskRequest) {
        LambdaQueryWrapper<RuTask> queryWrapper = new LambdaQueryWrapper<>();

        Long instanceId = ruTaskRequest.getInstanceId();
        queryWrapper.eq(ObjectUtil.isNotNull(instanceId), RuTask::getInstanceId, instanceId);

        List<Long> instanceIdList = ruTaskRequest.getInstanceIdList();
        queryWrapper.in(ObjectUtil.isNotEmpty(instanceIdList), RuTask::getInstanceId, instanceIdList);

        Long reNodeId = ruTaskRequest.getReNodeId();
        queryWrapper.eq(ObjectUtil.isNotNull(reNodeId), RuTask::getReNodeId, reNodeId);

        Long approver = ruTaskRequest.getApprover();
        queryWrapper.eq(ObjectUtil.isNotNull(approver), RuTask::getApprover, approver);

        Integer taskStatus = ruTaskRequest.getTaskStatus();
        queryWrapper.eq(ObjectUtil.isNotNull(taskStatus), RuTask::getTaskStatus, taskStatus);

        Integer taskType = ruTaskRequest.getTaskType();
        queryWrapper.eq(ObjectUtil.isNotNull(taskType), RuTask::getTaskType, taskType);

        String readFlag = ruTaskRequest.getReadFlag();
        queryWrapper.eq(ObjectUtil.isNotNull(readFlag), RuTask::getReadFlag, readFlag);

        return queryWrapper;
    }

    private boolean validateCopyTaskAlreadyExisted(Long approverId, Long instanceId) {
        LambdaQueryWrapper<RuTask> ruTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ruTaskLambdaQueryWrapper.eq(RuTask::getApprover, approverId);
        ruTaskLambdaQueryWrapper.eq(RuTask::getInstanceId, instanceId);
        ruTaskLambdaQueryWrapper.eq(RuTask::getTaskType, TaskTypeEnum.COPY.getCode());
        ruTaskLambdaQueryWrapper.eq(RuTask::getTaskStatus, TaskStatusEnum.PROCESSING.getCode());
        long count = this.count(ruTaskLambdaQueryWrapper);
        return count > 0;
    }
}