
package org.jeecg.modules.workFlow.flowlong.engine.model;

import org.jeecg.modules.workFlow.flowlong.engine.ModelInstance;
import org.jeecg.modules.workFlow.flowlong.engine.TaskTrigger;
import org.jeecg.modules.workFlow.flowlong.engine.assist.Assert;
import org.jeecg.modules.workFlow.flowlong.engine.assist.ObjectUtils;
import org.jeecg.modules.workFlow.flowlong.engine.core.Execution;
import org.jeecg.modules.workFlow.flowlong.engine.core.FlowLongContext;
import org.jeecg.modules.workFlow.flowlong.engine.core.enums.*;
import org.jeecg.modules.workFlow.flowlong.engine.entity.FlwProcess;
import org.jeecg.modules.workFlow.flowlong.engine.entity.FlwTaskActor;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * JSON BPM 节点
 *

 */
@Getter
@Setter
public class NodeModel implements ModelInstance, Serializable {

    /**
     * 节点名称
     */
    private String nodeName;
    /**
     * 节点 key
     */
    private String nodeKey;
    /**
     * 调用外部流程 {@link FlwProcess}
     * <p>
     * 实际业务存储格式 processId:processName 流程ID名称冒号拼接内容
     * </p>
     * <p>
     * 不存在冒号为 processKey 内容用于测试等场景
     * </p>
     */
    private String callProcess;
    /**
     * 是否异步调用【例如：子流程该参数为 true 则为异步子流程】
     */
    private Boolean callAsync;
    /**
     * 任务关联的表单url
     */
    private String actionUrl;
    /**
     * 节点类型 {@link TaskType}
     * <p>
     * -1，结束节点 0，发起人 1，审批人 2，抄送人 3，条件审批 4，条件分支 5，办理子流程 6，定时器任务 7，触发器任务 8，并发分支 9，包容分支
     * </p>
     * <p>
     * 23，路由分支 30，自动通过 31，自动拒绝
     * </p>
     */
    private Integer type;
    /**
     * 审核人类型 {@link NodeSetType}
     * <p>
     * 1，指定成员 2，主管 3，角色 4，发起人自选 5，发起人自己 6，连续多级主管 7，部门 8，指定候选人
     * </p>
     */
    private Integer setType;
    /**
     * 审核分配到任务的处理者，过 setType 区分个人角色或部门
     */
    private List<NodeAssignee> nodeAssigneeList;
    /**
     * 节点候选人或角色等
     */
    private NodeCandidate nodeCandidate;
    /**
     * 指定主管层级
     */
    private Integer examineLevel;

    /**
     * 多人审批时审批方式 {@link PerformType}
     * <p>
     * 0，发起 1，按顺序依次审批 2，会签 (可同时审批，每个人必须审批通过) 3，或签 (有一人审批通过即可) 4，票签 (总权重大于 50% 表示通过) <br/>
     * 5， 6，定时器 7，触发器 8， 9，抄送
     * </p>
     */
    private Integer examineMode;
    /**
     * 自定义连续主管审批层级
     */
    private Integer directorLevel;
    /**
     * 连续主管审批方式
     * <p>
     * 0，直到最上级主管 1，自定义审批终点
     * </p>
     */
    private Integer directorMode;
    /**
     * 发起人自选类型
     * <p>
     * 1，自选一个人 2，自选多个人 3，自选角色
     * </p>
     */
    private Integer selectMode;
    /**
     * 超时自动审批
     */
    private Boolean termAuto;
    /**
     * 审批期限（小时）
     */
    private Integer term;
    /**
     * 审批期限超时后执行类型
     * <p>
     * 0，自动通过 1，自动拒绝
     * </p>
     */
    private Integer termMode;
    /**
     * 审批类型 1，人工审批 2，自动通过 3，自动拒绝
     */
    private Integer typeOfApprove;
    /**
     * 通过权重（ 所有分配任务权重之和大于该值即通过，默认 50 ）
     */
    private Integer passWeight;
    /**
     * 驳回策略 1，驳回到发起人，2，驳回到上一节点，3，驳回到指定节点 4，终止审批流程 5，驳回到模型父节点
     */
    private Integer rejectStrategy;
    /**
     * 驳回重新审批策略 1，继续往下执行 2，回到上一个节点
     */
    private Integer rejectStart;
    /**
     * 条件节点列表
     */
    private List<ConditionNode> conditionNodes;
    /**
     * 并行节点
     * <p>相当于并行网关</p>
     */
    private List<NodeModel> parallelNodes;
    /**
     * 包容节点
     * <p>相当于包容网关</p>
     */
    private List<ConditionNode> inclusiveNodes;
    /**
     * 路由节点
     * <p>setType设置为 10 生效，根据对应条件自动跳转到指定路由节点位置</p>
     */
    private List<ConditionNode> routeNodes;
    /**
     * 审批提醒
     */
    private Boolean remind;
    /**
     * 允许发起人自选抄送人
     */
    private Boolean allowSelection;
    /**
     * 允许转交
     */
    private Boolean allowTransfer;
    /**
     * 允许加签/减签
     */
    private Boolean allowAppendNode;
    /**
     * 允许回退
     */
    private Boolean allowRollback;
    /**
     * 允许审批节点手动创建抄送任务
     */
    private Boolean allowCc;
    /**
     * 审批人与提交人为同一人时 {@link NodeApproveSelf}
     * <p>
     * 0，由发起人对自己审批 1，自动跳过 2，转交给直接上级审批 3，转交给部门负责人审批
     * </p>
     */
    private Integer approveSelf;
    /**
     * 扩展配置，用于存储表单权限、操作权限 等控制参数配置
     * <p>
     * 定时器任务：自定义参数 time 触发时间
     * </p>
     * <p>
     * 例如：一小时后触发 {"time": "1:h"} 单位【 d 天 h 时 m 分 】
     * </p>
     * <p>
     * 发起后一小时三十分后触发 {"time": "01:30:00"}
     * </p>
     */
    private Map<String, Object> extendConfig;
    /**
     * 子节点
     */
    private NodeModel childNode;
    /**
     * 父节点，模型 json 不存在该属性、属于逻辑节点
     */
    private NodeModel parentNode;
    /**
     * 触发器类型 1，立即执行 2，延迟执行
     */
    private String triggerType;
    /**
     * 延时处理类型 1，固定时长 2，自动计算
     * <p>
     * 固定时长 "time": "1:m"
     * </p>
     * <p>
     * 自动计算 "time": "17:02:53"
     * </p>
     */
    private String delayType;

    /**
     * 执行节点
     *
     * @param flowLongContext 流程引擎上下文
     * @param execution       执行对象
     * @return 执行结果 true 成功 false 失败
     */
    @Override
    public boolean execute(FlowLongContext flowLongContext, Execution execution) {

        if (TaskType.conditionBranch.eq(this.type)) {
            /*
             * 执行条件分支
             */
            flowLongContext.getFlowConditionHandler()
                    .getConditionNode(flowLongContext, execution, this)
                    .ifPresent(t -> this.executeConditionNode(flowLongContext, execution, t));
            return true;
        }

        if (TaskType.parallelBranch.eq(this.type)) {
            /*
             * 执行并行分支
             */
            for (NodeModel parallelNode : parallelNodes) {
                if (TaskType.conditionNode.eq(parallelNode.getType())) {
                    parallelNode.getChildNode().execute(flowLongContext, execution);
                } else {
                    parallelNode.execute(flowLongContext, execution);
                }
            }
            return true;
        }

        if (TaskType.inclusiveBranch.eq(this.type)) {
            /*
             * 执行包容分支
             */
            flowLongContext.getFlowConditionHandler()
                    .getInclusiveNodes(flowLongContext, execution, this)
                    .ifPresent(t -> t.forEach(s -> this.executeConditionNode(flowLongContext, execution, s)));
            return true;
        }

        if (TaskType.routeBranch.eq(this.type)) {
            /*
             * 执行路由分支
             */
            Optional<ConditionNode> routeNodeOptional = flowLongContext.getFlowConditionHandler()
                    .getRouteNode(flowLongContext, execution, this);
            if (routeNodeOptional.isPresent()) {
                // 自动跳转到指定节点
                execution.getEngine().executeJumpTask(execution.getFlwTask().getId(), routeNodeOptional.get().getNodeKey(),
                        execution.getFlowCreator(), execution.getArgs(), TaskType.routeJump);
            } else {
                // 执行子节点逻辑
                NodeModel _childNode = this.getChildNode();
                if (null != _childNode) {
                    _childNode.execute(flowLongContext, execution);
                }
            }
            return true;
        }

        /*
         * 执行 1、审批任务 2、创建抄送 5、办理子流程 6、定时器任务 7、触发器任务
         */
        if (TaskType.approval.eq(this.type) || TaskType.cc.eq(this.type)
                || TaskType.callProcess.eq(this.type) || TaskType.timer.eq(this.type)
                || TaskType.trigger.eq(this.type)) {

            // 创建任务
            flowLongContext.createTask(execution, this);
        }

        /*
         * 执行【自动通过】结束流程
         */
        else if (TaskType.autoPass.eq(this.type)) {
            return execution.endInstance(this, InstanceState.autoPass);
        }

        /*
         * 执行【自动拒绝】结束流程
         */
        else if (TaskType.autoReject.eq(this.type)) {
            return execution.endInstance(this, InstanceState.autoReject);
        }

        /*
         * 执行结束流程
         */
        else if (TaskType.end.eq(this.type)) {
            return execution.endInstance(this);
        }

        /*
         * 不存在子节点，不存在其它分支节点，当前执行节点为最后节点 并且当前节点不是审批节点
         * 执行结束流程处理器
         */
        if (null == this.getChildNode() && null == this.getConditionNodes()) {
            if (!this.nextNode().isPresent() && !TaskType.approval.eq(this.type)) {
                execution.endInstance(this);
            }
        }
        return true;
    }

    /**
     * 执行条件节点分支
     *
     * @param flowLongContext {@link FlowLongContext}
     * @param execution       {@link Execution}
     * @param conditionNode   {@link ConditionNode}
     */
    public void executeConditionNode(FlowLongContext flowLongContext, Execution execution, ConditionNode conditionNode) {
        NodeModel childNode = conditionNode.getChildNode();
        if (null == childNode) {
            // 当前条件节点无执行节点，进入当前执行条件节点的下一个节点
            childNode = this.getChildNode();
        }
        if (null != childNode) {
            childNode.execute(flowLongContext, execution);
        } else {
            // 查看是否存在其他的节点 fix https://gitee.com/aizuda/flowlong/issues/I9O8GV
            this.nextNode().ifPresent(nodeModel -> nodeModel.execute(flowLongContext, execution));
        }
    }

    /**
     * 获取process定义的指定节点key的节点模型
     *
     * @param nodeKey 节点key
     * @return 模型节点
     */
    public NodeModel getNode(String nodeKey) {
        if (Objects.equals(this.nodeKey, nodeKey)) {
            return this;
        }

        // 条件分支
        NodeModel fromConditionNode = this.getFromConditionNodes(nodeKey, conditionNodes);
        if (fromConditionNode != null) {
            return fromConditionNode;
        }

        // 并行分支
        NodeModel fromParallelNode = this.getFromNodeModels(nodeKey, parallelNodes);
        if (fromParallelNode != null) {
            return fromParallelNode;
        }

        // 包容分支
        NodeModel fromInclusiveNode = this.getFromConditionNodes(nodeKey, inclusiveNodes);
        if (fromInclusiveNode != null) {
            return fromInclusiveNode;
        }

        // 条件节点中没有找到 那么去它的同级子节点中继续查找
        if (null != childNode) {
            return childNode.getNode(nodeKey);
        }
        return null;
    }

    /**
     * 从节点列表中获取指定key节点信息
     *
     * @param nodeKey    节点 key
     * @param nodeModels 节点模型列表
     * @return 模型节点
     */
    private NodeModel getFromNodeModels(String nodeKey, List<NodeModel> nodeModels) {
        if (null != nodeModels) {
            for (NodeModel nodeModel : nodeModels) {
                NodeModel selectNode = nodeModel.getNode(nodeKey);
                if (null != selectNode) {
                    return selectNode;
                }
            }
        }
        return null;
    }

    /**
     * 从条件节点中获取节点
     *
     * @param nodeKey        节点 key
     * @param conditionNodes 条件节点模型列表
     * @return 模型节点
     */
    private NodeModel getFromConditionNodes(String nodeKey, List<ConditionNode> conditionNodes) {
        if (null != conditionNodes) {
            for (ConditionNode conditionNode : conditionNodes) {
                NodeModel conditionChildNode = conditionNode.getChildNode();
                if (null != conditionChildNode) {
                    NodeModel nodeModel = conditionChildNode.getNode(nodeKey);
                    if (null != nodeModel) {
                        return nodeModel;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 下一个执行节点
     *
     * @return 模型节点
     */
    public Optional<NodeModel> nextNode() {
        return nextNode(null);
    }

    /**
     * 下一个执行节点
     *
     * @param currentTask 当前任务
     * @return 模型节点
     */
    public Optional<NodeModel> nextNode(List<String> currentTask) {
        NodeModel nextNode = this.getChildNode();
        if (null == nextNode) {
            // 如果当前节点完成，并且该节点为条件节点，找到主干执行节点继续执行
            nextNode = ModelHelper.findNextNode(this, currentTask);
        }
        return Optional.ofNullable(nextNode);
    }

    /**
     * 是否异步调用
     */
    public boolean callAsync() {
        return null != callAsync && callAsync;
    }

    /**
     * 获取父审批节点
     *
     * @return 模型节点
     */
    public NodeModel parentApprovalNode() {
        NodeModel parentNode = this.getParentNode();
        if (parentNode.approvalOrMajor()) {
            // 父节点为审批节点或主办发起节点
            return parentNode;
        }

        // 继续找父审批节点
        return parentNode.parentApprovalNode();
    }

    /**
     * 判断是否为审批或主办节点
     *
     * @return true 是 false 否
     */
    public boolean approvalOrMajor() {
        return TaskType.approval.eq(type) || TaskType.major.eq(type);
    }

    /**
     * 判断是否为条件节点
     *
     * @return true 是 false 否
     */
    public boolean conditionNode() {
        return TaskType.conditionNode.eq(type) || TaskType.conditionBranch.eq(type);
    }

    /**
     * 判断是否为抄送节点
     *
     * @return true 是 false 否
     */
    public boolean ccNode() {
        return TaskType.cc.eq(type);
    }

    /**
     * 判断是否为并行节点
     *
     * @return true 是 false 否
     */
    public boolean parallelNode() {
        return TaskType.parallelBranch.eq(type);
    }

    /**
     * 判断是否为包容节点
     *
     * @return true 是 false 否
     */
    public boolean inclusiveNode() {
        return TaskType.inclusiveBranch.eq(type);
    }

    /**
     * 判断是否为路由节点
     *
     * @return true 是 false 否
     */
    public boolean routeNode() {
        return TaskType.routeBranch.eq(type);
    }

    /**
     * 判断是否为调用子流程节点
     *
     * @return true 是 false 否
     */
    public boolean callProcessNode() {
        return TaskType.callProcess.eq(type);
    }

    /**
     * 判断是否为结束节点
     *
     * @return true 是 false 否
     */
    public boolean endNode() {
        return TaskType.end.eq(type);
    }

    public NodeAssignee nextNodeAssignee(Execution execution, String assigneeId) {
        boolean findTaskActor = false;
        NodeAssignee nextNodeAssignee = null;
        List<NodeAssignee> nodeAssigneeList = this.getNodeAssigneeList();
        if (ObjectUtils.isEmpty(nodeAssigneeList)) {
            /*
             * 模型未设置处理人，那么需要获取自定义参与者
             */
            List<FlwTaskActor> taskActors = execution.getTaskActorProvider().getTaskActors(this, execution);
            if (ObjectUtils.isNotEmpty(taskActors)) {
                for (FlwTaskActor taskActor : taskActors) {
                    if (findTaskActor) {
                        // 找到下一个执行人
                        nextNodeAssignee = NodeAssignee.of(taskActor);
                        break;
                    }

                    // 判断找到当前任务实际办理人
                    if (Objects.equals(taskActor.getActorId(), assigneeId)) {
                        findTaskActor = true;
                    }
                }
            }
        } else {
            /*
             * 模型中去找下一个执行者
             */
            for (NodeAssignee nodeAssignee : nodeAssigneeList) {
                if (findTaskActor) {
                    // 找到下一个执行人
                    nextNodeAssignee = nodeAssignee;
                    break;
                }
                if (Objects.equals(nodeAssignee.getId(), assigneeId)) {
                    findTaskActor = true;
                }
            }
        }
        return nextNodeAssignee;
    }

    /**
     * 执行触发器
     *
     * @param execution {@link Execution}
     * @param supplier  执行默认触发器执行函数
     */
    public void executeTrigger(Execution execution, Supplier<Boolean> supplier) {
        boolean callSupplier = true;
        boolean flag = false;
        Map<String, Object> extendConfig = this.getExtendConfig();
        if (null != extendConfig) {
            Object _trigger = extendConfig.get("trigger");
            if (null != _trigger) {
                try {
                    callSupplier = false;
                    Class<?> triggerClass = Class.forName((String) _trigger);
                    if (TaskTrigger.class.isAssignableFrom(triggerClass)) {
                        TaskTrigger taskTrigger = (TaskTrigger) ObjectUtils.newInstance(triggerClass);
                        flag = taskTrigger.execute(this, execution);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        // 使用默认触发器
        if (null != supplier && callSupplier) {
            flag = supplier.get();
        }
        Assert.isFalse(flag, "trigger execute error");
    }

    /**
     * 克隆基本信息
     * <p>不包含节点信息内容，避免循环递归序列化异常</p>
     *
     * @return 节点模型
     */
    public NodeModel cloneBaseInfo() {
        NodeModel nodeModel = new NodeModel();
        nodeModel.setNodeName(this.nodeName);
        nodeModel.setNodeKey(this.nodeKey);
        nodeModel.setCallProcess(this.callProcess);
        nodeModel.setActionUrl(this.actionUrl);
        nodeModel.setType(this.type);
        nodeModel.setSetType(this.setType);
        nodeModel.setNodeAssigneeList(this.nodeAssigneeList);
        nodeModel.setNodeCandidate(this.nodeCandidate);
        nodeModel.setExamineLevel(this.examineLevel);
        nodeModel.setExamineMode(this.examineMode);
        nodeModel.setDirectorLevel(this.directorLevel);
        nodeModel.setDirectorMode(this.directorMode);
        nodeModel.setSelectMode(this.selectMode);
        nodeModel.setTermAuto(this.termAuto);
        nodeModel.setTerm(this.term);
        nodeModel.setTermMode(this.termMode);
        nodeModel.setTypeOfApprove(this.typeOfApprove);
        nodeModel.setPassWeight(this.passWeight);
        nodeModel.setRejectStrategy(this.rejectStrategy);
        nodeModel.setRejectStart(this.rejectStart);
        nodeModel.setRemind(this.remind);
        nodeModel.setAllowSelection(this.allowSelection);
        nodeModel.setAllowTransfer(this.allowTransfer);
        nodeModel.setAllowAppendNode(this.allowAppendNode);
        nodeModel.setAllowRollback(this.allowRollback);
        nodeModel.setApproveSelf(this.approveSelf);
        nodeModel.setExtendConfig(this.extendConfig);
        nodeModel.setTriggerType(this.triggerType);
        nodeModel.setDelayType(this.delayType);
        return nodeModel;
    }
}
