package cn.fateverse.workflow.service.impl;

import cn.fateverse.admin.entity.User;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.core.result.page.TableDataInfo;
import cn.fateverse.workflow.constant.ProcessConstant;
import cn.fateverse.workflow.entity.FormPerm;
import cn.fateverse.workflow.entity.HistoricalOperation;
import cn.fateverse.workflow.entity.ProcessData;
import cn.fateverse.workflow.entity.UserInstance;
import cn.fateverse.workflow.mapper.*;
import cn.fateverse.workflow.entity.bpmn.FormItem;
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
import cn.fateverse.workflow.entity.bpmn.UserInfo;
import cn.fateverse.workflow.entity.dto.ProcessInstanceDto;
import cn.fateverse.workflow.entity.vo.FormItemData;
import cn.fateverse.workflow.entity.vo.HistoricalOperationVo;
import cn.fateverse.workflow.entity.vo.ProcessInstanceDetailVo;
import cn.fateverse.workflow.entity.vo.ProcessInstanceVo;
import cn.fateverse.workflow.enums.FormItemEnum;
import cn.fateverse.workflow.enums.FormPermEnum;
import cn.fateverse.workflow.process.ProcessServiceUtils;
import cn.fateverse.workflow.service.ProcessInstanceService;
import cn.fateverse.workflow.service.ProcessNodeService;
import cn.fateverse.common.mybatis.utils.PageUtils;
import cn.fateverse.common.security.utils.SecurityUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author Clay
 * @date 2022/12/24
 */
@Slf4j
@Service
public class ProcessInstanceServiceImpl implements ProcessInstanceService {

    //任务服务
    private final TaskService taskService;
    //运行中的服务
    private final RuntimeService runtimeService;
    //资源服务
    private final RepositoryService repositoryService;
    //历史服务
    private final HistoryService historyService;
    //表单item数数据mapper
    private final FormItemDataMapper formItemDataMapper;
    //自定义历史操作
    private final HistoricalOperationMapper operationMapper;
    //流程数据
    private final ProcessDataMapper processDataMapper;
    //表单权限mapper
    private final FormPermMapper formPermMapper;
    //用户流程mapper
    private final UserInstanceMapper userInstanceMapper;
    //流程节点操作服务
    private final ProcessNodeService processNodeService;

    public ProcessInstanceServiceImpl(TaskService taskService,
                                      RuntimeService runtimeService,
                                      HistoryService historyService,
                                      RepositoryService repositoryService,
                                      FormItemDataMapper formItemDataMapper,
                                      HistoricalOperationMapper operationMapper,
                                      ProcessDataMapper processDataMapper,
                                      FormPermMapper formPermMapper,
                                      UserInstanceMapper userInstanceMapper,
                                      ProcessNodeService processNodeService) {
        this.taskService = taskService;
        this.runtimeService = runtimeService;
        this.repositoryService = repositoryService;
        this.historyService = historyService;
        this.formItemDataMapper = formItemDataMapper;
        this.processDataMapper = processDataMapper;
        this.operationMapper = operationMapper;
        this.formPermMapper = formPermMapper;
        this.userInstanceMapper = userInstanceMapper;
        this.processNodeService = processNodeService;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void start(ProcessInstanceDto processInstanceDto) {
        ProcessInstance processInstance = getProcessInstance(processInstanceDto, processInstanceDto.getProcessDefinitionId());
        //创建用户实例
        UserInstance userInstance = UserInstance.builder()
                .instanceId(processInstance.getProcessInstanceId())
                .processInstanceId(processInstance.getProcessInstanceId())
                .deploymentName(processInstance.getProcessDefinitionName())
                .state(ProcessConstant.BUSINESS_STATUS_DEAL)
                .build();
        userInstance.setCreateBy(userInstance.getInitiator());
        userInstance.setUserId(SecurityUtils.getUserId().toString());
        userInstanceMapper.insert(userInstance);
    }

    @Override
    public void restart(ProcessInstanceDto processInstanceDto) {
        UserInstance userInstance = userInstanceMapper.selectByInstanceId(processInstanceDto.getProcessDefinitionId());
        if (null == userInstance || userInstance.getState().equals(ProcessConstant.BUSINESS_STATUS_DEAL)
                || !userInstance.getUserId().equals(SecurityUtils.getUserId().toString())) {
            throw new CustomException("当前节点状态不允许!");
        }
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(userInstance.getProcessInstanceId())
                .singleResult();
        ProcessInstance processInstance = getProcessInstance(processInstanceDto, historicProcessInstance.getProcessDefinitionId());
        userInstance.setProcessInstanceId(processInstance.getProcessInstanceId());
        userInstance.setState(ProcessConstant.BUSINESS_STATUS_DEAL);
        userInstanceMapper.update(userInstance);
    }

    @NotNull
    private ProcessInstance getProcessInstance(ProcessInstanceDto processInstanceDto, String processDefinitionId) {
        //获取到当前key对应的流程对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();
        //todo 流程实例启动时需要的参数
        Map<String, Object> variables = new HashMap<>(16);
        //表单参数
        variables.put(ProcessConstant.FORM_VAR, processInstanceDto.getFormData());
        //用户自选参数
        if (null != processInstanceDto.getOptionalUser() && !processInstanceDto.getOptionalUser().isEmpty()) {
            variables.put(ProcessConstant.OPTIONAL_USER, processInstanceDto.getOptionalUser());
        }
        //用户信息
        variables.put(ProcessConstant.START_USER_INFO, Objects.requireNonNull(SecurityUtils.getLoginUser()).getUser());
        //流程状态
        variables.put(ProcessConstant.PROCESS_STATUS, ProcessConstant.BUSINESS_STATUS_DEAL);
        //将用户信息传入,用于后边判断发起人是谁的分支判断条件
        variables.put(ProcessConstant.START_USER_ID, SecurityUtils.getUserId());
        Map<String, Object> fromValue = JSONObject.parseObject(processInstanceDto.getFormData(), new TypeReference<Map<String, Object>>() {
        });
        //插入表单数据项
        variables.putAll(fromValue);
        //启动流程对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                .processDefinitionId(processDefinitionId)
                .variables(variables)
                .name(processDefinition.getName())
                .start();
        String processInstanceId = processInstance.getId();
        //对表单数据进行解析
        //入库表单数据
        FormItemData data = FormItemData.builder()
                .isStart(Boolean.TRUE)
                .isPresent(Boolean.TRUE)
                .processInstanceId(processInstanceId)
                .formData(processInstanceDto.getFormData())
                .createBy(SecurityUtils.getUsername())
                .build();
        //将自选用户添加到历史数据中
        if (null != processInstanceDto.getOptionalUser()) {
            data.setOptionalUser(JSON.toJSONString(processInstanceDto.getOptionalUser()));
        }
        formItemDataMapper.insert(data);
        return processInstance;
    }

    /**
     * todo 需要检查分页是否生效
     *
     * @return
     */
    @Override
    public TableDataInfo<ProcessInstanceVo> searchList(String state) {
        PageUtils.startPage();
        List<UserInstance> userInstanceList = userInstanceMapper.selectByUserId(null, state, null);
        long total = new PageInfo<>(userInstanceList).getTotal();
        return processingUserInstanceList(userInstanceList, total);
    }

    @Override
    public TableDataInfo<ProcessInstanceVo> searchSelfList(String state, String deploymentName) {
        PageUtils.startPage();
        List<UserInstance> userInstanceList = userInstanceMapper.selectByUserId(SecurityUtils.getUserId().toString(), state, deploymentName);
        long total = new PageInfo<>(userInstanceList).getTotal();
        return processingUserInstanceList(userInstanceList, total);
    }

    @Override
    public TableDataInfo<ProcessInstanceVo> searchAboutMeList(String state, String deploymentName) {
        PageUtils.startPage();
        List<UserInstance> userInstanceList = userInstanceMapper.selectProcessInstanceListByUserIdAndState(SecurityUtils.getUserId(), state, deploymentName);
        long total = new PageInfo<>(userInstanceList).getTotal();
        return processingUserInstanceList(userInstanceList, total);
    }

    @Override
    public ProcessInstanceDetailVo searchSelfInfo(String processInstanceId) {
        //查询出当前实例对应的表单数据
        FormItemData formItemData = formItemDataMapper.selectByProcessInstanceId(processInstanceId, true);
        JSONObject formData = JSON.parseObject(formItemData.getFormData());
        //获取到流程实例对昂
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .includeProcessVariables()
                .singleResult();
        //获取到bpmn附带的数据
        Map<String, String> attribute = ProcessServiceUtils.getBpmnValueAttribute(repositoryService, processInstance.getProcessDefinitionId());
        //获取到流程用户数据
        Map<String, Object> variables = processInstance.getProcessVariables();
        //获取用户对象
        User user = (User) variables.get(ProcessConstant.START_USER_INFO);
        //获取到操作历史
        List<HistoricalOperationVo> operationList = ProcessServiceUtils.getHistoricalOperations(processInstanceId, operationMapper);
        //获取到流程数据
        List<ProcessNode> process = processNodeService.getProcessNodes(attribute.get(ProcessConstant.BPMN_FORM_PROCESS), user, operationList,
                (JSONObject) variables.get(ProcessConstant.OPTIONAL_USER), false);
        //流程中所有的节点信息
        Set<String> processIdSet = process.stream().map(ProcessNode::getId).collect(Collectors.toSet());
        //表单数据转换
        List<FormItem> formItems = JSON.parseArray(attribute.get(ProcessConstant.BPMN_FORM_ITEMS), FormItem.class);
        //将用户数据返回
        UserInfo userInfo = UserInfo.toUserInfo(user);
        //初始化基础对象
        ProcessInstanceDetailVo detailVo = ProcessInstanceDetailVo.builder()
                .formData(formData)
                .process(process)
                .userInfo(userInfo)
                .operationList(operationList)
                .runningList(new ArrayList<>())
                .noTakeList(new ArrayList<>())
                .refuseList(new ArrayList<>())
                .passList(new ArrayList<>())
                .endList(new ArrayList<>())
                .build();
        //获取到正在运行的节点
        Set<String> runningList = new HashSet<>();
        //获取到已经运行结束的节点
        Set<String> endList = new HashSet<>();
        //获取到拒绝的节点
        Set<String> refuseList = new HashSet<>();
        //直接通过的节点
        Set<String> passList = new HashSet<>();
        operationList.forEach(operation -> {
            switch (operation.getState()) {
                case RUNNING:
                    runningList.add(operation.getNodeId());
                    processIdSet.remove(operation.getNodeId());
                    break;
                case AGREE:
                case CREATE:
                case AUTO_PASS:
                    endList.add(operation.getNodeId());
                    processIdSet.remove(operation.getNodeId());
                    break;
                case REFUSE:
                    refuseList.add(operation.getNodeId());
                    processIdSet.remove(operation.getNodeId());
                    break;
                case PASS:
                    passList.add(operation.getNodeId());
                    processIdSet.remove(operation.getNodeId());
                    break;
            }
        });
        List<FormItem> result;
        if (ProcessConstant.BUSINESS_STATUS_DEAL.equals(variables.get(ProcessConstant.PROCESS_STATUS))) {
            //获取到当前的运行节点
            List<Task> taskList = taskService.createTaskQuery()
                    .processInstanceId(processInstanceId)
                    .list();
            //拿到当前运行的formKey,用于后边进行表单权限合并
            List<String> formKeys = taskList.stream().map(Task::getFormKey).collect(Collectors.toList());
            Set<String> formPermSet = null;
            if (!formKeys.isEmpty()) {
                //获取到当前运行节点的表单权限数据
                List<FormPerm> formPerms = formPermMapper.selectListByFormKeyListAndDeploymentId(formKeys, processInstance.getDeploymentId());
                formPermSet = formPerms.stream().filter(formPerm -> null != formPerm.getPerm() && formPerm.getPerm().equals(FormPermEnum.READ)).map(FormPerm::getId).collect(Collectors.toSet());
                //筛选出可读的所有表单id
                //初始化表单权限
            }
            result = initFormItems(formItems, formPermSet);
        } else {
            result = initFormItems(formItems, null);
        }
        //将剩下的参数填入到表单当中
        detailVo.setRunningList(new ArrayList<>(runningList));
        detailVo.setEndList(new ArrayList<>(endList));
        detailVo.setNoTakeList(new ArrayList<>(processIdSet));
        detailVo.setRefuseList(new ArrayList<>(refuseList));
        detailVo.setPassList(new ArrayList<>(passList));
        detailVo.setFormItems(result);
        return detailVo;
    }

    @Override
    public ProcessInstanceDetailVo searchReInfo(String instanceId) {
        UserInstance userInstance = userInstanceMapper.selectByInstanceId(instanceId);
        if (null == userInstance || userInstance.getState().equals(ProcessConstant.BUSINESS_STATUS_DEAL)
                || !userInstance.getUserId().equals(SecurityUtils.getUserId().toString())) {
            throw new CustomException("当前节点状态不允许!");
        }
        HistoricProcessInstanceEntityImpl historicProcessInstance = (HistoricProcessInstanceEntityImpl) historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(userInstance.getProcessInstanceId())
                .singleResult();
        //表单数据
        FormItemData formItemData = formItemDataMapper.selectByProcessInstanceId(historicProcessInstance.getProcessInstanceId(), Boolean.TRUE);
        JSONObject formData = JSON.parseObject(formItemData.getFormData());
        //表单项数据
        ProcessData processData = processDataMapper.selectByProcessDefinitionKey(historicProcessInstance.getProcessDefinitionKey());
        List<FormItem> formItems = JSON.parseArray(processData.getFormItemStr(), FormItem.class);
        //流程数据
        User user = Objects.requireNonNull(SecurityUtils.getLoginUser()).getUser();
        //流程数据
        List<ProcessNode> process = processNodeService.getProcessNodes(processData.getProcessStr(), user, new ArrayList<>(),
                new JSONObject(), false);
        //流程节点上的自选数据
        JSONObject optionalUser = JSONObject.parseObject(formItemData.getOptionalUser());
        return ProcessInstanceDetailVo.builder()
                .process(process)
                .formData(formData)
                .formItems(formItems)
                .optionalUser(optionalUser)
                .build();
    }

    /**
     * 初始化表单数据
     *
     * @param formItems   表单项数据
     * @param formPermSet 表单权限
     * @return 处理后的表单数据
     */
    private List<FormItem> initFormItems(List<FormItem> formItems, Set<String> formPermSet) {
        //初始化接受对象
        List<FormItem> result = new ArrayList<>();
        //对当前的表单项进行判断
        for (FormItem formItem : formItems) {
            //判断是否是分栏布局独享
            if (FormItemEnum.SpanLayout.getName().equals(formItem.getName())) {
                JSONObject props = formItem.getProps();
                List<FormItem> items = props.getList("items", FormItem.class);
                props.put("items", initFormItems(items, formPermSet));
            }
            //进行全新校验,如果set集合中没有参数,则认为当前表单为可读,
            if (null != formPermSet && !formPermSet.isEmpty()) {
                //如果当前表单中存在则设置可读,如果没有,则直接丢弃当前表单,不传递给前端
                if (formPermSet.contains(formItem.getId())) {
                    formItem.setPerm(FormPermEnum.READ.getType());
                    result.add(formItem);
                }
            } else {
                formItem.setPerm(FormPermEnum.READ.getType());
                result.add(formItem);
            }
        }
        return result;
    }

    /**
     * 将HistoricProcessInstance组装成需要返回的数据
     *
     * @param userInstanceList 用户实例
     * @param count            总数
     * @return view需要的渲染数据
     */
    private TableDataInfo<ProcessInstanceVo> processingUserInstanceList(List<UserInstance> userInstanceList, long count) {
        //如果为空,则直接返回空列表
        if (null == userInstanceList || userInstanceList.isEmpty()) {
            return PageUtils.getDataTable(new ArrayList<>());
        }
        Long userId = SecurityUtils.getUserId();
        //获取到流程中的流程实例id
        List<String> processInstanceListIds = userInstanceList.stream().map(UserInstance::getProcessInstanceId).collect(Collectors.toList());
        List<HistoricalOperation> operationAllList;
        if (processInstanceListIds.isEmpty()) {
            operationAllList = new ArrayList<>();
        } else {
            //获取数据库中正在运行中的状态
            operationAllList = operationMapper.selectRunningByProcessInstanceId(processInstanceListIds);
        }
        //对获取到的历史记录进行映射
        Map<String, List<HistoricalOperation>> operationMap = operationAllList.stream().collect(Collectors.groupingBy(HistoricalOperation::getProcessInstanceId));
        //开始流程Vo的映射
        List<ProcessInstanceVo> processInstanceVoList = userInstanceList.stream().map(instance -> {
            //获取到历史节点中正在运行的节点
            List<HistoricalOperation> operationList = operationMap.get(instance.getProcessInstanceId());
            ProcessInstanceVo processInstanceVo = ProcessInstanceVo.builder()
                    .taskId(instance.getInstanceId())
                    .deploymentName(instance.getDeploymentName())
                    .processInstanceId(instance.getProcessInstanceId())
                    .state(instance.getState())
                    .initiator(instance.getInitiator())
                    .submitTime(instance.getSubmitTime())
                    .endTime(instance.getFinishTime())
                    .isSelf(instance.getUserId().equals(userId.toString()))
                    .build();
            //如果获取到当前运行中的节点不为空,则需要修改审批人和当前任务节点
            if (null != operationList && !operationList.isEmpty() && instance.getState().equals(ProcessConstant.BUSINESS_STATUS_DEAL)) {
                List<String> taskNameList = new ArrayList<>();
                List<String> approveNameList = new ArrayList<>();
                operationList.forEach(operation -> {
                    taskNameList.add(operation.getOperationName());
                    List<UserInfo> userInfos = JSON.parseArray(operation.getUserInfo(), UserInfo.class);
                    if (null != userInfos && !userInfos.isEmpty()) {
                        approveNameList.addAll(userInfos.stream().map(UserInfo::getName).collect(Collectors.toList()));
                    }
                });
                processInstanceVo.setTaskName(taskNameList.toString());
                processInstanceVo.setApproveName(approveNameList.toString());
            } else if (instance.getState().equals(ProcessConstant.BUSINESS_STATUS_REVOCATION)) {
                processInstanceVo.setApproveName(null);
                processInstanceVo.setTaskName("撤销");
            } else if (instance.getState().equals(ProcessConstant.BUSINESS_STATUS_REJECT)) {
                processInstanceVo.setApproveName(null);
                processInstanceVo.setTaskName("驳回");
            } else if (instance.getState().equals(ProcessConstant.BUSINESS_STATUS_ENDED)) {
                processInstanceVo.setApproveName(null);
                processInstanceVo.setTaskName("完成");
            }
            return processInstanceVo;
        }).collect(Collectors.toList());
        //返回分页信息
        return PageUtils.convertDataTable(processInstanceVoList, count);
    }

}
