package com.hz.pm.api.todocenter;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.exception.BizException;
import com.hz.pm.api.AppTests;
import com.wflow.bean.entity.WflowCcTasks;
import com.wflow.mapper.WflowCcTasksMapper;
import com.wflow.utils.CodeUtil;
import com.wflow.workflow.bean.dto.ReqProcessHandlerDTO;
import com.wflow.workflow.enums.ProcessHandlerEnum;
import com.wflow.workflow.mapper.CustomHisActInstMapper;
import com.wflow.workflow.utils.ProcessTaskUtils;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.common.engine.impl.cmd.CustomSqlExecution;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.cmd.AbstractCustomSqlExecution;
import org.flowable.engine.runtime.Execution;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Classname FlowableTest
 * @Description
 * @Date 2023/5/14 9:12
 * @Author PoffyZhang
 */
@Slf4j
public class FlowableTest extends AppTests {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private WflowCcTasksMapper ccTasksMapper;

    @Test
    public void withdrawTest(){
        String taskId = "cc3d7536-f1f3-11ed-b9e0-02426daa406d";

        // 不是流程发起人撤回
        HistoricTaskInstance handledTaskInstance = historyService.createHistoricTaskInstanceQuery()
                .taskId(taskId)
                .singleResult();
        if (Objects.isNull(handledTaskInstance)){
            throw new BizException("获取任务实例失败!");
        }

        String processInstanceId = handledTaskInstance.getProcessInstanceId();

        // 获取已处理历史活跃实例节点ID
        String handledNodeId = handledTaskInstance.getTaskDefinitionKey();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(handledTaskInstance.getProcessDefinitionId());

        //先得到 其所在子流程
        Map<String, String> subNodeMap = ProcessTaskUtils.getSubNodeMap(bpmnModel);
        String subProcessId = subNodeMap.get(handledNodeId);
        //然后得到 子流程下的所有 nodeId
        Map<String, List<String>> allSubNodeMap = ProcessTaskUtils.loadSubNodeMap(bpmnModel);
        List<String> nodeIds = allSubNodeMap.get(subProcessId);

        // 获取待处理历史活跃实例节点ID(会签/或签会有多个)
        // 获取当前流程实例待审核任务信息
        List<Task> taskList = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .taskDefinitionKeys(nodeIds)
                .list();
        //保证是此子流程下的任务 那第一个就是当前操作节点了
        Task currentTask = taskList.get(0);
        String currentNodeId = currentTask.getTaskDefinitionKey();

        // 传节点定义key 获取撤回操作人在流程配置中所在的节点
        FlowElement beforeFlowElement = bpmnModel.getFlowElement(handledNodeId);
        // 获取执行撤回操作的节点所在子流程所有节点ID
        List<String> actIdList = Lists.newArrayList(handledNodeId,currentNodeId);

        List<HistoricActivityInstance> historicActivityInstanceList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();

        // 标记执行撤回操作节点及当前待处理节点(userTask类型)为被撤回(使用DELETE_REASON_标志)
        List<HistoricActivityInstance> activityInstances = historicActivityInstanceList.stream()
                .filter(a -> actIdList.contains(a.getActivityId()))
                .collect(Collectors.toList());

        // 先查出当前撤回节点所在子流程节点的节点ID
        List<String> subProcessActIds = historicActivityInstanceList.stream()
                .filter(a -> "subProcess".equals(a.getActivityType()))
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toList());
        List<Execution> executions = runtimeService.createExecutionQuery()
                .processInstanceId(processInstanceId).onlyChildExecutions().list();
        List<String> actIds = executions.stream().map(Execution::getActivityId).collect(Collectors.toList());
        // 去除子流程节点ID
        actIds.removeAll(subProcessActIds);
        // 强制流程指向前一个审核人节点
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(processInstanceId)
                .moveActivityIdsToSingleActivityId(actIds,handledNodeId)
                .changeState();

        // 如果是从会签节点撤回,执行会签节点的节点ID和当前待处理节点ID相同,只需要标记当前会签节点为被撤回
        if (handledNodeId.equals(currentNodeId)){
            activityInstances = activityInstances.stream().filter(a -> currentNodeId.equals(a.getActivityId())).collect(Collectors.toList());
        }
        for (HistoricActivityInstance activityInstance : activityInstances) {
            String executionId = activityInstance.getExecutionId();
            // 备注子流程已处理节点为被撤回
            CustomSqlExecution<CustomHisActInstMapper, Integer> customSqlExecution = new AbstractCustomSqlExecution<CustomHisActInstMapper, Integer>(CustomHisActInstMapper.class) {
                @Override
                public Integer execute(CustomHisActInstMapper customHisActInstMapper) {
                    // 更新历史活跃实例表中删除原因为被撤回
                    return customHisActInstMapper.deleteHisActInst(processInstanceId, executionId);
                }
            };
            managementService.executeCustomSql(customSqlExecution);
            taskService.deleteTask(activityInstance.getTaskId());
            historyService.deleteHistoricTaskInstance(activityInstance.getTaskId());
        }
    }

    @Test
    public void testWithDraw(){
        ReqProcessHandlerDTO param = new ReqProcessHandlerDTO();
        param.setProjectId(633L);
        param.setTaskId("8e3e9329-f544-11ed-91fd-02426daa406d");
        param.setAction(ProcessHandlerEnum.WITHDRAW);
        param.setInstanceId("93877c57-f542-11ed-91fd-02426daa406d");

        // 不是流程发起人撤回
        HistoricTaskInstance handledTaskInstance = historyService.createHistoricTaskInstanceQuery()
                .taskId(param.getTaskId())
                .singleResult();
        if (Objects.isNull(handledTaskInstance)){
            throw new BizException("获取任务实例失败!");
        }

        String processInstanceId = handledTaskInstance.getProcessInstanceId();

        // 获取已处理历史活跃实例节点ID
        String handledNodeId = handledTaskInstance.getTaskDefinitionKey();
        Date handleCreateTime = handledTaskInstance.getCreateTime();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(handledTaskInstance.getProcessDefinitionId());

        //先得到 其所在子流程
        Map<String, String> subNodeMap = ProcessTaskUtils.getSubNodeMap(bpmnModel);
        String subProcessId = subNodeMap.get(handledNodeId);
        //然后得到 子流程下的所有 nodeId
        Map<String, List<String>> allSubNodeMap = ProcessTaskUtils.loadSubNodeMap(bpmnModel);
        List<String> nodeIds = allSubNodeMap.get(subProcessId);

        // 获取待处理历史活跃实例节点ID(会签/或签会有多个)
        // 获取当前流程实例待审核任务信息
        List<Task> taskList = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .taskDefinitionKeys(nodeIds)
                .list();
        //保证是此子流程下的任务 那第一个就是当前操作节点了
        Task currentTask = taskList.get(0);
        Date currentCreateTime = currentTask.getCreateTime();
        String currentNodeId = currentTask.getTaskDefinitionKey();

        // 传节点定义key 获取撤回操作人在流程配置中所在的节点
        FlowElement beforeFlowElement = bpmnModel.getFlowElement(handledNodeId);
        // 获取执行撤回操作的节点所在子流程所有节点ID
        List<String> actIdList = Lists.newArrayList(handledNodeId,currentNodeId);

        List<HistoricActivityInstance> historicActivityInstanceList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();

        // 标记执行撤回操作节点及当前待处理节点(userTask类型)为被撤回(使用DELETE_REASON_标志)
        List<HistoricActivityInstance> activityInstances = historicActivityInstanceList.stream()
                .filter(a -> {
                    if(actIdList.contains(a.getActivityId())){
                        if(a.getActivityId().equals(handledNodeId)){
                            return (CodeUtil.computationTime(a.getStartTime(),handleCreateTime) == 0L);
                        }else if(a.getActivityId().equals(currentNodeId)){
                            return (CodeUtil.computationTime(a.getStartTime(),currentCreateTime) == 0L);
                        }
                    }
                    return Boolean.FALSE;
                })
                .collect(Collectors.toList());

        // 先查出当前撤回节点所在子流程节点的节点ID
        List<String> subProcessActIds = historicActivityInstanceList.stream()
                .filter(a -> "subProcess".equals(a.getActivityType()))
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toList());
        List<Execution> executions = runtimeService.createExecutionQuery()
                .processInstanceId(processInstanceId).onlyChildExecutions().list();
        List<String> actIds = executions.stream().map(Execution::getActivityId).collect(Collectors.toList());
        // 去除子流程节点ID
        actIds.removeAll(subProcessActIds);
        // 强制流程指向前一个审核人节点
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(processInstanceId)
//                .moveActivityIdTo(currentNodeId,handledNodeId)
                .moveActivityIdsToSingleActivityId(actIds,handledNodeId)
                .changeState();

        // 如果是从会签节点撤回,执行会签节点的节点ID和当前待处理节点ID相同,只需要标记当前会签节点为被撤回
        if (handledNodeId.equals(currentNodeId)){
            activityInstances = activityInstances.stream().filter(a -> currentNodeId.equals(a.getActivityId())).collect(Collectors.toList());
        }
        for (HistoricActivityInstance activityInstance : activityInstances) {
            String executionId = activityInstance.getExecutionId();
            // 备注子流程已处理节点为被撤回
            CustomSqlExecution<CustomHisActInstMapper, Integer> customSqlExecution = new AbstractCustomSqlExecution<CustomHisActInstMapper, Integer>(CustomHisActInstMapper.class) {
                @Override
                public Integer execute(CustomHisActInstMapper customHisActInstMapper) {
                    // 更新历史活跃实例表中删除原因为被撤回
                    return customHisActInstMapper.deleteHisActInst(processInstanceId, executionId);
                }
            };
            managementService.executeCustomSql(customSqlExecution);
            taskService.deleteTask(activityInstance.getTaskId());
            historyService.deleteHistoricTaskInstance(activityInstance.getTaskId());
        }
        // 如果当前执行撤回操作的节点后有抄送节点，撤回后，需将抄送任务节点删除
        String ccNodeId = param.getCcNodeId();
        ccTasksMapper.delete(Wrappers.lambdaQuery(WflowCcTasks.class).eq(WflowCcTasks::getNodeId,ccNodeId));
    }

    @Test
    public void reject(){
        String instCode = "0e63faec-08ff-11ee-9663-00163e000353";
        String nodeId1 = "node_6486ddcbe4b0ab4a27ccfdbd";
        String nodeId2 = "node_6486ddcbe4b0ab4a27ccfdc1";
        String nodeId3 = "node_6486ddcbe4b0ab4a27ccfdc3";

        List<Execution> executions = runtimeService.createExecutionQuery()
                .processInstanceId(instCode)
                .onlyChildExecutions().list();
        //强制流程指向驳回
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(instCode)
                .moveActivityIdsToSingleActivityId(Lists.newArrayList(nodeId1,nodeId2,nodeId3), "refuse-end")
                .moveExecutionsToSingleActivityId(executions.stream().map(Execution::getId)
                        .collect(Collectors.toList()), "refuse-end")
                .changeState();

        log.info("操作成功!");
    }
}
