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.auth.api.context.LoginContext;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.workflow.api.NodeApi;
import cn.stylefeng.workflow.api.WorkflowApi;
import cn.stylefeng.workflow.api.entity.design.Node;
import cn.stylefeng.workflow.api.entity.task.RuTask;
import cn.stylefeng.workflow.api.enums.ApproverNodeTypeEnum;
import cn.stylefeng.workflow.api.enums.TaskStatusEnum;
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.util.WorkFlowStatusUtil;
import cn.stylefeng.workflow.instance.api.InstanceVariableApi;
import cn.stylefeng.workflow.instance.api.pojo.task.RuTaskRequest;
import cn.stylefeng.workflow.instance.modular.task.enums.RuTaskExceptionEnum;
import cn.stylefeng.workflow.instance.modular.task.pojo.operate.DelegateTaskByInstanceIdRequest;
import cn.stylefeng.workflow.instance.modular.task.pojo.operate.DelegateTaskRequest;
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.taskopt.TaskDelegateService;
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.List;
import java.util.Map;

@Service
public class TaskDelegateServiceImpl implements TaskDelegateService {

    @Resource
    private RuTaskService ruTaskService;

    @Resource
    private TaskDaoWithEvent taskDaoWithEvent;

    @Resource
    private InstanceVariableApi instanceVariableApi;

    @Resource
    private NodeApi nodeApi;

    @Resource
    private WorkflowApi workflowApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delegate(DelegateTaskRequest delegateTaskRequest) {

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

        taskDaoWithEvent.updateTaskStatus(ruTask, TaskStatusEnum.TRANSFERRED.getCode(), delegateTaskRequest.getSuggestContent());

        this.createNewRuTask(delegateTaskRequest.getUserId(), ruTask);
    }

    @Override
    public void updatePointNodeUserId(Node currentNode, RuTask originRuTask, RuTask newTransferTask, Long instanceId, InstanceVariable instanceVariable) {

        NodeData nodeData = currentNode.getNodeData();
        if (nodeData == null) {
            return;
        }

        ApproverConfig approverConfig = nodeData.getApproverConfig();
        if (approverConfig == null) {
            return;
        }

        if (!approverConfig.getApproverStrategy().getApproverNodeType().equals(ApproverNodeTypeEnum.ONE_BY_ONE.getMode())) {
            return;
        }

        Long originApprover = originRuTask.getApprover();
        Long newTransferApprover = newTransferTask.getApprover();
        if (originApprover.equals(newTransferApprover)) {
            return;
        }

        if (ObjectUtil.isEmpty(instanceVariable)) {
            return;
        }
        Map<Long, List<Long>> pointNodeApproverUserId = instanceVariable.getPointNodeApproverUserId();
        if (ObjectUtil.isEmpty(pointNodeApproverUserId)) {
            return;
        }

        List<Long> pointUserIdList = pointNodeApproverUserId.get(currentNode.getNodeId());
        if (ObjectUtil.isEmpty(pointUserIdList)) {
            return;
        }
        pointUserIdList.set(pointUserIdList.indexOf(originApprover), newTransferApprover);

        this.instanceVariableApi.updateInstanceVariable(instanceId, instanceVariable);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delegateByInstanceId(DelegateTaskByInstanceIdRequest delegateTaskByInstanceIdRequest) {

        List<RuTask> userRunningTaskList = workflowApi.getInstanceRunningTasks(
                delegateTaskByInstanceIdRequest.getInstanceId(),
                LoginContext.me().getLoginUser().getUserId());
        if (ObjectUtil.isEmpty(userRunningTaskList)) {
            throw new ServiceException(RuTaskExceptionEnum.CANT_DELEGATE_TASK);
        }

        for (RuTask ruTask : userRunningTaskList) {
            taskDaoWithEvent.updateTaskStatus(ruTask, TaskStatusEnum.TRANSFERRED.getCode(), delegateTaskByInstanceIdRequest.getSuggestContent());
        }

        RuTask oldRuTask = userRunningTaskList.get(0);

        this.createNewRuTask(delegateTaskByInstanceIdRequest.getUserId(), oldRuTask);
    }

    private void createNewRuTask(Long newRuTaskUserId, RuTask oldRuTask) {
        RuTask newTask = new RuTask();
        BeanUtil.copyProperties(oldRuTask, newTask);
        newTask.setTaskId(IdWorker.getId());

        newTask.setApprover(newRuTaskUserId);

        WorkFlowStatusUtil.initTaskStatus(newTask, oldRuTask.getTaskType());

        newTask.setSuggestTime(null);
        newTask.setSuggestContent(null);

        this.taskDaoWithEvent.saveTask(newTask);

        InstanceVariable instanceVariableValue = instanceVariableApi.getInstanceVariableValue(newTask.getInstanceId());
        Node nodeInfo = nodeApi.getNodeInfo(newTask.getReNodeId());
        this.updatePointNodeUserId(nodeInfo, oldRuTask, newTask, newTask.getInstanceId(), instanceVariableValue);
    }

}
