package cn.fateverse.workflow.service.impl;

import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.core.result.page.TableDataInfo;
import cn.fateverse.common.mybatis.utils.PageUtils;
import cn.fateverse.workflow.constant.ProcessConstant;
import cn.fateverse.workflow.entity.FormPerm;
import cn.fateverse.workflow.entity.ProcessBusiness;
import cn.fateverse.workflow.entity.ProcessData;
import cn.fateverse.workflow.entity.ProcessListener;
import cn.fateverse.workflow.entity.bpmn.FormItem;
import cn.fateverse.workflow.entity.bpmn.ListenerInfo;
import cn.fateverse.workflow.entity.bpmn.ListenerItem;
import cn.fateverse.workflow.entity.dto.ProcessDto;
import cn.fateverse.workflow.entity.query.ProcessQuery;
import cn.fateverse.workflow.entity.vo.ProcessDefinitionHistoryVo;
import cn.fateverse.workflow.entity.vo.ProcessDefinitionInfoVo;
import cn.fateverse.workflow.entity.vo.ProcessDefinitionVo;
import cn.fateverse.workflow.enums.ProcessNodeEnum;
import cn.fateverse.workflow.mapper.FormPermMapper;
import cn.fateverse.workflow.mapper.ProcessBusinessMapper;
import cn.fateverse.workflow.mapper.ProcessDataMapper;
import cn.fateverse.workflow.mapper.ProcessListenerMapper;
import cn.fateverse.workflow.service.ProcessDefinitionService;
import cn.fateverse.workflow.process.BpmnParseUtil;
import cn.fateverse.workflow.process.FormItemParseUtil;
import cn.fateverse.workflow.service.ProcessNodeService;
import cn.fateverse.common.security.utils.SecurityUtils;
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
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/23
 */
@Slf4j
@Service
public class ProcessDefinitionServiceImpl implements ProcessDefinitionService {

    private final RepositoryService repositoryService;

    private final ProcessBusinessMapper processBusinessMapper;

    private final ProcessDataMapper processDataMapper;

    private final FormPermMapper formPermMapper;

    private final ProcessNodeService processNodeService;

    private final ProcessListenerMapper listenerMapper;


    public ProcessDefinitionServiceImpl(RepositoryService repositoryService,
                                        ProcessBusinessMapper processBusinessMapper,
                                        ProcessDataMapper processDataMapper,
                                        FormPermMapper formPermMapper,
                                        ProcessNodeService processNodeService, ProcessListenerMapper listenerMapper) {
        this.repositoryService = repositoryService;
        this.processBusinessMapper = processBusinessMapper;
        this.processDataMapper = processDataMapper;
        this.formPermMapper = formPermMapper;
        this.processNodeService = processNodeService;
        this.listenerMapper = listenerMapper;
    }

    @Override
    public TableDataInfo<ProcessDefinitionVo> searchList(ProcessQuery query) {
        PageUtils.startPage();
        List<ProcessDefinitionVo> list = processBusinessMapper.selectWorkflowBusinessSimple(query);
        return PageUtils.getDataTable(list);
    }

    @Override
    public List<ProcessDefinitionHistoryVo> searchHistoryList(String processDefinitionKey) {
        return processDataMapper.selectByDeploymentKey(processDefinitionKey);
    }

    @Override
    public ProcessDefinitionInfoVo searchByDeploymentId(String deploymentId) {
        //获取流程数据
        ProcessData processData = processDataMapper.selectByDeploymentId(deploymentId);
        //获取流程业务数据
        ProcessBusiness processBusiness = processBusinessMapper.selectByDeploymentId(deploymentId);
        if (Objects.isNull(processBusiness)) {
            throw new CustomException("当前流程id有无,获取数据失败!");
        }
        //组装数据
        ProcessDefinitionInfoVo definitionInfoVo = ProcessDefinitionInfoVo.builder()
                .processDefinitionKey(processBusiness.getProcessDefinitionKey())
                .deploymentName(processBusiness.getDeploymentName())
                .build();
        definitionInfoVo.setLogo(JSON.parse(processData.getLogo()));
        definitionInfoVo.setSettings(JSON.parse(processData.getSettings()));
        definitionInfoVo.setProcess(JSON.parseArray(processData.getProcessStr(), ProcessNode.class));
        definitionInfoVo.setFormItems(JSON.parseArray(processData.getFormItemStr(), FormItem.class));
        return definitionInfoVo;
    }

    @Override
    public ProcessDefinitionInfoVo searchInitiateInfoByKey(String processDefinitionKey) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
        if (processDefinition.isSuspended()) {
            throw new CustomException("当前流程已停用");
        }
        //获取流程数据
        ProcessBusiness processBusiness = processBusinessMapper.selectByDeploymentKey(processDefinitionKey);
        //获取流程业务数据
        ProcessData processData = processDataMapper.selectByDeploymentId(processBusiness.getDeploymentId());
        //获取权限列表
        Map<String, FormPerm> formPermMap = formPermMapper.selectListByFormKeyAndDeploymentId(ProcessConstant.ROOT_NODE, processData.getDeploymentId());
        //进行权限判断
        List<FormItem> formItems = FormItemParseUtil.checkFromItemPermsNew(JSON.parseArray(processData.getFormItemStr(), FormItem.class), formPermMap, null);
        ProcessDefinitionInfoVo definitionInfoVo = ProcessDefinitionInfoVo.builder()
                .processDefinitionKey(processBusiness.getProcessDefinitionKey())
                .deploymentName(processBusiness.getDeploymentName())
                .build();
        definitionInfoVo.setLogo(JSON.parse(processData.getLogo()));
        definitionInfoVo.setSettings(JSON.parse(processData.getSettings()));
        List<ProcessNode> process = processNodeService.getProcessNodes(processData.getProcessStr(), SecurityUtils.getLoginUser().getUser(), new ArrayList<>(), new JSONObject(), true);
        definitionInfoVo.setProcess(process);
        definitionInfoVo.setFormItems(formItems);
        return definitionInfoVo;
    }

    @Override
    public void suspend(String processDefinitionId) {
        repositoryService.suspendProcessDefinitionById(processDefinitionId);
    }

    @Override
    public void activate(String processDefinitionId) {
        repositoryService.activateProcessDefinitionById(processDefinitionId);
    }

    @Override
    public void delete(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, false);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void add(ProcessDto processDto) {
        //获取到node节点数据
        List<ProcessNode> processNodeList = processDto.getProcess();
        Set<String> listenerIdSet = new HashSet<>();
        processNodeList.stream().filter(node ->
                node.getType() == ProcessNodeEnum.APPROVAL
                        || node.getType() == ProcessNodeEnum.TRIGGER
                        || node.getType() == ProcessNodeEnum.CC
                        || node.getType() == ProcessNodeEnum.DELAY).forEach(node -> {
            ListenerInfo listener = node.getProps().getListener();
            if (listener.getState()) {
                listener.getList().stream().filter(ListenerItem::getIsSys).forEach(item -> {
                    listenerIdSet.add(item.getListenerValue());
                });
            }
        });
        List<ProcessListener> processListeners = null;
        if (!listenerIdSet.isEmpty()) {
            processListeners = listenerMapper.selectIds(new ArrayList<>(listenerIdSet));
        }
        //开始解析节点 todo 还需要解析处每一个节点所需要的动态字段数据,并且也需要入库存放与一张新表中,或者在表单权限中进行存储
        BpmnParseUtil util = new BpmnParseUtil(processDto, processListeners);
        BpmnModel bpmnModel = util.run();
        //查询当前key在系统中是否有过部署记录
        ProcessBusiness processBusiness = processBusinessMapper.selectByDeploymentKey(processDto.getProcessDefinitionKey());
        //当系统中key有部署记录是,deploymentName直接使用系统中的,不允许做其他更改
        String deploymentName;
        if (null == processBusiness) {
            deploymentName = processDto.getDeploymentName();
        } else {
            deploymentName = processBusiness.getDeploymentName();
        }
        //部署流程
        Deployment deployment = repositoryService.createDeployment()
                .addBpmnModel(deploymentName + ".bpmn", bpmnModel)
                .name(deploymentName)
                .deploy();
        //设置流程数据
        processDto.setDeploymentId(deployment.getId());
        //当前key在系统中有过部署则更新数据,没有则新增数据
        if (null == processBusiness) {
            processBusinessMapper.insert(processDto);
        } else {
            processDto.setBusinessId(processBusiness.getBusinessId());
            processBusinessMapper.update(processDto);
        }
        //构建当前部署流程的附带数据
        ProcessData processData = ProcessData.builder()
                .deploymentId(deployment.getId())
                .deploymentName(processDto.getDeploymentName())
                .formItemStr(JSON.toJSONString(processDto.getFormItems()))
                .processStr(JSON.toJSONString(processNodeList))
                .logo(JSON.toJSONString(processDto.getLogo()))
                .settings(JSON.toJSONString(processDto.getSettings()))
                .remark(processDto.getRemark())
                .build();
        //新增附带数据
        processDataMapper.insert(processData);
        //获取到当前流程中对应节点的表单权限数据
        List<FormPerm> formPerms = util.getFormPerms();
        //判断是否为空,不为空则进行入库
        if (!formPerms.isEmpty()) {
            formPerms = formPerms.stream().peek(fromPerm -> fromPerm.setDeploymentId(deployment.getId())).collect(Collectors.toList());
            formPermMapper.batchInsert(formPerms);
        }
    }
}
