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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.workflow.api.NodeApi;
import cn.stylefeng.workflow.api.entity.design.Node;
import cn.stylefeng.workflow.api.entity.instance.RuntimeNode;
import cn.stylefeng.workflow.api.entity.task.RuTask;
import cn.stylefeng.workflow.api.enums.AddSignTypeEnum;
import cn.stylefeng.workflow.api.enums.ApproverNodeTypeEnum;
import cn.stylefeng.workflow.api.enums.TaskStatusEnum;
import cn.stylefeng.workflow.api.enums.TaskTypeEnum;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import cn.stylefeng.workflow.api.pojo.InstanceVariable;
import cn.stylefeng.workflow.api.util.WorkFlowStatusUtil;
import cn.stylefeng.workflow.instance.api.InstanceVariableApi;
import cn.stylefeng.workflow.instance.api.WorkflowStatusApi;
import cn.stylefeng.workflow.instance.api.pojo.task.RuTaskRequest;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeNodeService;
import cn.stylefeng.workflow.instance.modular.task.enums.NodeApproverExceptionEnum;
import cn.stylefeng.workflow.instance.modular.task.pojo.operate.AddSignRequest;
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.agree.base.AgreeOneByOneStrategy;
import cn.stylefeng.workflow.instance.modular.task.taskopt.TaskAddSignService;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class TaskAddSignServiceImpl implements TaskAddSignService {

    @Resource
    private RuTaskService ruTaskService;

    @Resource
    private TaskDaoWithEvent taskDaoWithEvent;

    @Resource
    private NodeApi nodeApi;

    @Resource
    private WorkflowStatusApi workflowStatusApi;

    @Resource
    private RuntimeNodeService runtimeNodeService;

    @Resource
    private InstanceVariableApi instanceVariableApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSign(AddSignRequest addSignRequest) {

        RuTaskRequest ruTaskRequest = new RuTaskRequest();
        ruTaskRequest.setTaskId(addSignRequest.getTaskId());
        RuTask ruTask = this.ruTaskService.detail(ruTaskRequest);


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

        Integer approverNodeType = nodeInfo.getNodeData().getApproverConfig().getApproverStrategy().getApproverNodeType();
        if (ApproverNodeTypeEnum.SINGLE.getMode().equals(approverNodeType)) {
            throw new ServiceException(NodeApproverExceptionEnum.SINGLE_CANT_ADD_SIGN);
        }

        Integer addSignType = addSignRequest.getAddSignType();
        if (ObjectUtil.isNotEmpty(addSignType)) {

            if (addSignType.equals(AddSignTypeEnum.BEFORE_ADD_SIGN.getCode())) {
                this.beforeAddSign(ruTask, nodeInfo, addSignRequest.getAddSignUserIdList());
            }

            else if (addSignType.equals(AddSignTypeEnum.AFTER_ADD_SIGN.getCode())) {
                this.afterAddSign(ruTask, nodeInfo, addSignRequest.getAddSignUserIdList());
            }
            return;
        }


        List<RuTask> addSignTaskList = new ArrayList<>();
        for (Long userId : addSignRequest.getAddSignUserIdList()) {
            RuTask addTask = new RuTask();
            BeanUtil.copyProperties(ruTask, addTask);

            addTask.setTaskId(IdWorker.getId());

            addTask.setApprover(userId);

            WorkFlowStatusUtil.initTaskStatus(addTask, ruTask.getTaskType());

            addSignTaskList.add(addTask);
        }

        this.taskDaoWithEvent.batchSaveTaskList(addSignTaskList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void beforeAddSign(RuTask ruTask, Node nodeInfo, List<Long> addSignUserIdList) {

        taskDaoWithEvent.updateTaskStatus(ruTask, TaskStatusEnum.CLOSE.getCode(), "前加签任务，关闭当前任务。");

        InstanceContext instanceContext = workflowStatusApi.buildInstanceContext(ruTask.getInstanceId());
        List<Long> thisNodeTotalUserIdList = new AgreeOneByOneStrategy(false).getPointedUserIdList(instanceContext, ruTask);
        if (ObjectUtil.isEmpty(thisNodeTotalUserIdList)) {
            return;
        }

        Long currentApprover = ruTask.getApprover();
        int currentApproverIndex = thisNodeTotalUserIdList.indexOf(currentApprover);

        thisNodeTotalUserIdList.addAll(currentApproverIndex, addSignUserIdList);
        this.updatePointUserIds(ruTask, instanceContext, thisNodeTotalUserIdList);

        Long nextUserId = addSignUserIdList.get(0);

        RuntimeNode runtimeNode = runtimeNodeService.getById(ruTask.getRuNodeId());
        ruTaskService.createUserTask(nextUserId, TaskTypeEnum.PROCESSING, runtimeNode, instanceContext);

        instanceVariableApi.updateVariableJson(instanceContext);
    }

    @Override
    public void afterAddSign(RuTask ruTask, Node nodeInfo, List<Long> addSignUserIdList) {

        InstanceContext instanceContext = workflowStatusApi.buildInstanceContext(ruTask.getInstanceId());
        List<Long> thisNodeTotalUserIdList = new AgreeOneByOneStrategy(false).getPointedUserIdList(instanceContext, ruTask);
        if (ObjectUtil.isEmpty(thisNodeTotalUserIdList)) {
            return;
        }

        Long currentApprover = ruTask.getApprover();
        int currentApproverIndexAfter = thisNodeTotalUserIdList.indexOf(currentApprover) + 1;

        thisNodeTotalUserIdList.addAll(currentApproverIndexAfter, addSignUserIdList);

        this.updatePointUserIds(ruTask, instanceContext, thisNodeTotalUserIdList);
        instanceVariableApi.updateVariableJson(instanceContext);
    }

    private void updatePointUserIds(RuTask ruTask, InstanceContext instanceContext, List<Long> newPointUserIds) {
        InstanceVariable instanceVariable = instanceContext.getInstanceVariable();
        Map<Long, List<Long>> pointNodeApproverUserId = instanceVariable.getPointNodeApproverUserId();
        pointNodeApproverUserId.put(ruTask.getReNodeId(), newPointUserIds);
    }

}
