package com.xjrsoft.module.workflow.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.EnabledMark;
import com.xjrsoft.common.enums.WorkflowMultiInstanceType;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.module.organization.entity.User;
import com.xjrsoft.module.workflow.constant.WorkflowConstant;
import com.xjrsoft.module.workflow.dto.HistoryChangeDto;
import com.xjrsoft.module.workflow.entity.WorkflowRecord;
import com.xjrsoft.module.workflow.entity.WorkflowSchema;
import com.xjrsoft.module.workflow.entity.WorkflowSchemaHistory;
import com.xjrsoft.module.workflow.mapper.WorkflowSchemaHistoryMapper;
import com.xjrsoft.module.workflow.mapper.WorkflowSchemaMapper;
import com.xjrsoft.module.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.module.workflow.service.IWorkflowRecordService;
import com.xjrsoft.module.workflow.service.IWorkflowSchemaHistoryService;
import com.xjrsoft.module.workflow.utils.WorkFlowUtil;
import com.xjrsoft.module.workflow.vo.ProcessChangeVo;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricVariableInstance;
import org.camunda.bpm.engine.impl.persistence.entity.HistoricVariableInstanceEntity;
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
import org.camunda.bpm.engine.migration.MigrationPlan;
import org.camunda.bpm.engine.migration.MigrationPlanExecutionBuilder;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.spring.SpringProcessEngineConfiguration;
import org.camunda.bpm.engine.task.Task;
import org.camunda.commons.utils.IoUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 流程模板历史记录表 服务实现类
 * </p>
 *
 * @author tzx
 * @since 2022-11-16
 */
@Service
@AllArgsConstructor
public class WorkflowSchemaHistoryServiceImpl extends MPJBaseServiceImpl<WorkflowSchemaHistoryMapper, WorkflowSchemaHistory> implements IWorkflowSchemaHistoryService {

    private final WorkflowSchemaHistoryMapper workflowSchemaHistoryMapper;

    private final RepositoryService repositoryService;

    private final WorkflowSchemaMapper workflowSchemaMapper;

    private final HistoryService historyService;

    private final RuntimeService runtimeService;

    private final TaskService taskService;

    private final SpringProcessEngineConfiguration processEngineConfiguration;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<ProcessChangeVo> change(HistoryChangeDto dto) {
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectById(dto.getSchemaId());

        WorkflowSchemaHistory history = getById(dto.getId());
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(history.getJsonContent(), WorkflowSchemaConfig.class);

        WorkflowSchema newWorkflowSchema = BeanUtil.toBean(workflowSchemaConfig.getProcessConfig(), WorkflowSchema.class);

        newWorkflowSchema.setId(workflowSchema.getId());
        newWorkflowSchema.setXmlContent(history.getXmlContent());
        newWorkflowSchema.setJsonContent(history.getJsonContent());

        //找到历史记录当前活动流程
//        WorkflowSchemaHistory activeHistory = getOne(Wrappers.lambdaQuery(WorkflowSchemaHistory.class).eq(WorkflowSchemaHistory::getSchemaId, dto.getSchemaId()).eq(WorkflowSchemaHistory::getActivityFlag, YesOrNoEnum.YES.getCode()));



        //找到所有非当前所选历史记录的所有数据
        List<WorkflowSchemaHistory> list = list(Wrappers.lambdaQuery(WorkflowSchemaHistory.class).eq(WorkflowSchemaHistory::getSchemaId, dto.getSchemaId()).ne(WorkflowSchemaHistory::getId, dto.getId()).select(WorkflowSchemaHistory::getDefinitionId,WorkflowSchemaHistory::getDefinitionKey));


        //将所有历史记录设置为 非活动版本
        WorkflowSchemaHistory paramObj = new WorkflowSchemaHistory();
        paramObj.setActivityFlag(EnabledMark.DISABLED.getCode());
        LambdaQueryWrapper<WorkflowSchemaHistory> wrapper = Wrappers.lambdaQuery(WorkflowSchemaHistory.class).eq(WorkflowSchemaHistory::getSchemaId, dto.getSchemaId());
        workflowSchemaHistoryMapper.update(paramObj, wrapper);

        //将当前所选history 设置为活动版本
        wrapper.eq(WorkflowSchemaHistory::getId, dto.getId());
        paramObj.setActivityFlag(EnabledMark.ENABLED.getCode());
        workflowSchemaHistoryMapper.update(paramObj, wrapper);

        try {
            for (WorkflowSchemaHistory workflowSchemaHistory : list) {

                MigrationPlan migrationPlan = runtimeService
                        .createMigrationPlan(workflowSchemaHistory.getDefinitionId(), history.getDefinitionId())
                        .mapEqualActivities()
                        .updateEventTriggers()
                        .build();
                //找到当前流程模板的正在运行的所有流程
                List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processDefinitionId(workflowSchemaHistory.getDefinitionId()).active().list();
                if (processInstances.size() > 0) {
                    List<String> processInstanceIds = processInstances.stream().map(ProcessInstance::getProcessInstanceId).collect(Collectors.toList());

                    MigrationPlanExecutionBuilder migrationPlanExecutionBuilder = runtimeService.newMigration(migrationPlan).processInstanceIds(processInstanceIds)
                            .skipCustomListeners()
                            .skipIoMappings();
                    migrationPlanExecutionBuilder.execute();
                }

            }
        }
        catch (Exception e){
            throw new MyException("当前流程图过于复杂，并且流程历史记录的模板，有正在进行的流程，不适用于迁移，请等待进行的流程完结！");
        }



        List<ProcessInstance> newProcesses = runtimeService.createProcessInstanceQuery().processDefinitionId(history.getDefinitionId()).list();


        String[] processIds = newProcesses.stream().map(ProcessInstance::getProcessInstanceId).toArray(String[]::new);

        List<HistoricVariableInstance> variableInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(processIds).variableName(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY).list();

        List<Task> taskList = taskService.createTaskQuery().processInstanceIdIn(processIds).list();

        List<ProcessChangeVo> voList = new ArrayList<>(newProcesses.size());


        for (ProcessInstance newProcess : newProcesses) {
            ProcessChangeVo vo = new ProcessChangeVo();
            vo.setName(newWorkflowSchema.getName());

            HistoricVariableInstance historicVariableInstance = variableInstanceList.stream().filter(x -> x.getProcessInstanceId().equals(newProcess.getProcessInstanceId())).findFirst().orElse(new HistoricVariableInstanceEntity());
            vo.setSerailNumber(Convert.toInt(historicVariableInstance.getValue()));

            vo.setStatus(1);

            Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(newProcess.getProcessInstanceId())).findFirst().orElse(new TaskEntity());

            //获取当前用户的信息
            User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
            Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
            IWorkflowRecordService workflowRecordService = SpringUtil.getBean(IWorkflowRecordService.class);
            //新增流程发起流程记录
            WorkflowRecord record = new WorkflowRecord();
            record.setNodeId(task.getId());
            record.setNodeName(task.getName());
            record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
            record.setProcessId(task.getProcessInstanceId());
            record.setSchemaId(schemaId);
            record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
            record.setRecordTime(LocalDateTime.now());

            //如果当前任务的节点被删除 默认将此任务完成
            if (workflowSchemaConfig.getChildNodeConfig().stream().noneMatch(x -> x.get(WorkflowConstant.NODE_CONFIG_ID_KEY).equals(task.getTaskDefinitionKey()))) {
                taskService.complete(task.getId());

                List<Task> nextTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
                List<String> nextTaskNameList = nextTaskList.stream().map(Task::getName).collect(Collectors.toList());
                vo.setDetail("流程已经变更到" + StrUtil.join(StringPool.COMMA, nextTaskNameList) + "节点");

                //[操作人名称]对流程进行了变更，流程当前节点为[节点名称]。
                record.setMessage("【" + user.getName() + "】对流程进行了变更，流程当前节点为【" + StrUtil.join("、", nextTaskNameList) + "】");
                workflowRecordService.save(record);
            } else {
                vo.setDetail("流程已经变更到" + task.getName() + "节点");

                //[操作人名称]对流程进行了变更，流程当前节点为[节点名称]。
                record.setMessage("【" + user.getName() + "】对流程进行了变更，流程当前节点为【" + task.getName() + "】");
                workflowRecordService.save(record);
            }

            voList.add(vo);
        }


        //更新的流程重新部署
        Deployment deploy = repositoryService.createDeployment()
                .addInputStream(newWorkflowSchema.getName() + StringPool.DOT + WorkflowConstant.WORKFLOW_SUFFIX, IoUtil.stringAsInputStream(newWorkflowSchema.getXmlContent())).name(newWorkflowSchema.getName())
                .deploy();

        newWorkflowSchema.setDeploymentId(deploy.getId());
        workflowSchemaMapper.updateById(newWorkflowSchema);


        //缓存节点监听器数据
        CompletableFuture.runAsync(() -> {
            WorkFlowUtil.removeNodeListener(workflowSchema.getDeploymentId());
            WorkFlowUtil.cacheNodeListener(deploy.getId(), workflowSchemaConfig.getChildNodeConfig());
        });



        return voList;
    }

    @Override
    public List<ProcessChangeVo> processChanges(HistoryChangeDto dto) {
        return null;
    }
}
