package com.fjhx.service.process.impl;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.constants.FlowConstant;
import com.fjhx.constants.FlowExplainConstant;
import com.fjhx.entity.process.ProcessNode;
import com.fjhx.entity.process.ProcessNodeButton;
import com.fjhx.enums.ButtonNameEnum;
import com.fjhx.enums.ProcessNodeTypeEnum;
import com.fjhx.mapper.process.ProcessNodeButtonMapper;
import com.fjhx.params.NodeDetailsVo;
import com.fjhx.service.process.ProcessNodeButtonService;
import com.fjhx.service.process.ProcessNodeService;
import com.fjhx.utils.Assert;
import org.springblade.core.log.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-08-12
 */
@Service
public class ProcessNodeButtonServiceImpl extends ServiceImpl<ProcessNodeButtonMapper, ProcessNodeButton> implements ProcessNodeButtonService {

    @Autowired
    private ProcessNodeService processNodeService;

    @Override
    public void insertNodeButton(NodeDetailsVo nodeDetailsVo) {

        List<ProcessNode> processNodeList = nodeDetailsVo.getProcessNodeList();

        // 赋值节点id
        for (ProcessNode processNode : processNodeList) {
            Integer type = processNode.getType();

            // 获取节点类型枚举值
            ProcessNodeTypeEnum processNodeTypeEnum = ProcessNodeTypeEnum.get(type);

            Assert.notEmpty(processNodeTypeEnum, FlowExplainConstant.NODE_UNKNOWN);

            // 开始节点编码
            if (processNodeTypeEnum.equals(ProcessNodeTypeEnum.START)) {
                processNode.setCode(FlowConstant.START_CODE);
            }

            // 结束节点编码
            else if (processNodeTypeEnum.equals(ProcessNodeTypeEnum.END)) {
                processNode.setCode(FlowConstant.END_CODE);
            }

            processNode.setProcessTenantId(nodeDetailsVo.getProcessTenantId());

            long newId = IdWorker.getId();

            Long oldId = processNode.getId();
            processNode.setId(newId);

            for (ProcessNode node : processNodeList) {
                if (node.getParentId().equals(oldId)) {
                    node.setParentId(newId);
                }
            }

        }

        // 按父级节点id分组
        Map<Long, List<ProcessNode>> processNodeGroupByParentId = processNodeList.stream()
                .collect(Collectors.groupingBy(ProcessNode::getParentId));

        // 按节点id分组
        Map<Long, List<ProcessNode>> processNodeGroupById = processNodeList.stream()
                .collect(Collectors.groupingBy(ProcessNode::getId));

        // 根据节点类型生成功能按钮
        List<ProcessNodeButton> processNodeButtonList = new ArrayList<>();

        for (ProcessNode processNode : processNodeList) {

            // 获取节点类型枚举值
            ProcessNodeTypeEnum processNodeTypeEnum = ProcessNodeTypeEnum.get(processNode.getType());

            switch (processNodeTypeEnum) {

                case START: // 开始
                    startHandle(processNodeButtonList, processNodeGroupById, processNodeGroupByParentId, processNode);
                    break;

                case EXAMINED: // 审核
                    examinedHandle(processNodeButtonList, processNodeGroupById, processNodeGroupByParentId, processNode);
                    break;

                case APPROVAL: // 审批
                    approvalHandle(processNodeButtonList, processNodeGroupByParentId, processNode);
                    break;

                case HANDLE: // 办理
                    handle(processNodeButtonList, processNodeGroupByParentId, processNode);
                    break;

                case BRANCH: // 分支
                    handleBranch(processNodeButtonList, processNode);
                    break;

                case END: // 结束
                    endHandle(processNodeGroupByParentId, processNode);
                    break;

            }
        }

        processNodeService.saveBatch(processNodeList);

        saveBatch(processNodeButtonList);
    }


    /**
     * 开始节点处理
     */
    private void startHandle(List<ProcessNodeButton> processNodeButtonList,
                             Map<Long, List<ProcessNode>> processNodeGroupById,
                             Map<Long, List<ProcessNode>> processNodeGroupByParentId,
                             ProcessNode processNode) {

        List<ProcessNode> previousProcessNodeList = processNodeGroupById.get(processNode.getParentId());
        if (previousProcessNodeList != null) {
            throw new ServiceException(String.format(FlowExplainConstant.PREVIOUS_ERROR, processNode.getName()));
        }

        // 下一步
        ProcessNodeButton next = next(processNodeGroupByParentId, processNode, ButtonNameEnum.START);
        processNodeButtonList.add(next);
    }

    /**
     * 审核处理
     */
    private void examinedHandle(List<ProcessNodeButton> processNodeButtonList,
                                Map<Long, List<ProcessNode>> processNodeGroupById,
                                Map<Long, List<ProcessNode>> processNodeGroupByParentId,
                                ProcessNode processNode) {
        // 通过
        ProcessNodeButton next = next(processNodeGroupByParentId, processNode, ButtonNameEnum.ADOPT);
        processNodeButtonList.add(next);
        // 驳回
        ProcessNodeButton previous = previous(processNodeGroupById, processNode, ButtonNameEnum.FAIL);
        processNodeButtonList.add(previous);
    }

    /**
     * 审批处理
     */
    private void approvalHandle(List<ProcessNodeButton> processNodeButtonList,
                                Map<Long, List<ProcessNode>> processNodeGroupByParentId, ProcessNode processNode) {
        // 同意
        ProcessNodeButton next = next(processNodeGroupByParentId, processNode, ButtonNameEnum.AGREE);
        processNodeButtonList.add(next);
        // 驳回
        ProcessNodeButton over = over(processNode, ButtonNameEnum.REJECT);
        processNodeButtonList.add(over);
    }

    /**
     * 办理处理
     */
    private void handle(List<ProcessNodeButton> processNodeButtonList,
                        Map<Long, List<ProcessNode>> processNodeGroupByParentId, ProcessNode processNode) {
        // 完成
        ProcessNodeButton next = next(processNodeGroupByParentId, processNode, ButtonNameEnum.COMPLETE);
        processNodeButtonList.add(next);
    }

    /**
     * 分支
     */
    private void handleBranch(List<ProcessNodeButton> processNodeButtonList, ProcessNode processNode) {
        ProcessNodeButton processNodeButton = createProcessNodeButton(processNode, ButtonNameEnum.BRANCH);
        processNodeButtonList.add(processNodeButton);
    }

    /**
     * 结束
     */
    private void endHandle(Map<Long, List<ProcessNode>> processNodeGroupByParentId, ProcessNode processNode) {
        List<ProcessNode> nextProcessNodeList = processNodeGroupByParentId.get(processNode.getId());
        if (nextProcessNodeList != null) {
            throw new ServiceException(String.format(FlowExplainConstant.JUMP_EMPTY, processNode.getName()));
        }
    }


    /**
     * 跳转上一节点
     */
    private ProcessNodeButton previous(Map<Long, List<ProcessNode>> processNodeGroupById,
                                       ProcessNode processNode, ButtonNameEnum buttonNameEnum) {

        ProcessNodeButton processNodeButton = createProcessNodeButton(processNode, buttonNameEnum);

        // 赋值跳转到上一节点
        List<ProcessNode> previousProcessNodeList = processNodeGroupById.get(processNode.getParentId());
        if (previousProcessNodeList == null || previousProcessNodeList.size() != 1) {
            throw new ServiceException(String.format(FlowExplainConstant.PREVIOUS_ERROR, processNode.getName()));
        }

        // 如果上一节点是分支，再往上一层级流转
        ProcessNode node = previousProcessNodeList.get(0);
        if (node.getType().equals(ProcessNodeTypeEnum.BRANCH.getType())) {

            previousProcessNodeList = processNodeGroupById.get(node.getParentId());
            if (previousProcessNodeList == null || previousProcessNodeList.size() != 1) {
                throw new ServiceException(String.format(FlowExplainConstant.PREVIOUS_ERROR, processNode.getName()));
            }

            processNodeButton.setJumpNodeId(previousProcessNodeList.get(0).getId());
            processNodeButton.setJumpNodeCode(previousProcessNodeList.get(0).getCode());
        } else {
            processNodeButton.setJumpNodeId(node.getId());
            processNodeButton.setJumpNodeCode(node.getCode());
        }

        return processNodeButton;
    }

    /**
     * 跳转下一节点
     */
    private ProcessNodeButton next(Map<Long, List<ProcessNode>> processNodeGroupByParentId,
                                   ProcessNode processNode, ButtonNameEnum buttonNameEnum) {
        ProcessNodeButton processNodeButton = createProcessNodeButton(processNode, buttonNameEnum);

        // 赋值跳转到下一节点
        List<ProcessNode> nextProcessNodeList = processNodeGroupByParentId.get(processNode.getId());
        if (nextProcessNodeList == null || nextProcessNodeList.size() != 1) {
            throw new ServiceException(String.format(FlowExplainConstant.JUMP_EMPTY, processNode.getName()));
        }

        ProcessNode itemProcessNode = nextProcessNodeList.get(0);
        processNodeButton.setJumpNodeId(itemProcessNode.getId());
        processNodeButton.setJumpNodeCode(itemProcessNode.getCode());
        return processNodeButton;
    }

    /**
     * 结束流程
     */
    private ProcessNodeButton over(ProcessNode processNode, ButtonNameEnum buttonNameEnum) {
        ProcessNodeButton processNodeButton = createProcessNodeButton(processNode, buttonNameEnum);
        processNodeButton.setJumpNodeId(FlowConstant.OVER_PROCESS_FLAG);
        processNodeButton.setJumpNodeCode(FlowConstant.END_CODE);
        return processNodeButton;
    }

    /**
     * 创建按钮
     */
    private ProcessNodeButton createProcessNodeButton(ProcessNode processNode, ButtonNameEnum buttonNameEnum) {
        ProcessNodeButton processNodeButton = new ProcessNodeButton();
        processNodeButton.setProcessTenantId(processNode.getProcessTenantId());
        processNodeButton.setProcessNodeId(processNode.getId());
        processNodeButton.setProcessNodeCode(processNode.getCode());
        processNodeButton.setNameType(buttonNameEnum.getType());
        return processNodeButton;
    }

}
