package cn.axzo.workflow.core.engine.listener;

import cn.axzo.workflow.common.constant.BpmnConstants;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.deletage.BpmnTaskAssigneeSelector;
import cn.axzo.workflow.core.deletage.BpmnTaskCalculateDTO;
import cn.axzo.workflow.core.deletage.BpmnTaskDelegate;
import cn.axzo.workflow.core.deletage.MockTaskAssigneeSelector;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_ALLOW_SKIP_USER_TASK;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_BUSINESS;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverEmptyHandleType;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecify;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNodeType;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getProcessServerVersion;


/**
 * 每一个任务节点创建 Execution 时执行的监听, 用于 BPMN 文件中为节点设置指定类型监听
 * <p>
 * 在当前工程中,主要是在这里设置审批人
 */
@Component
@Slf4j
public class EngineExecutionStartListener implements ExecutionListener {
    @Resource
    private ObjectProvider<BpmnTaskDelegate> bpmTaskDelegate;
    @Resource
    private List<BpmnTaskAssigneeSelector> selectors;
    @Resource
    private ObjectProvider<MockTaskAssigneeSelector> objectProvider;

    @Override
    public void notify(DelegateExecution execution) {
        log.info("execution Start: {}", JSON.toJSONString(execution));
        String currentActivityId = execution.getCurrentActivityId();
        String assigneeListVariableName = INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + currentActivityId;
        if (execution.hasVariable(assigneeListVariableName)) {
            return;
        }

        Process mainProcess = ProcessDefinitionUtil.getBpmnModel(execution.getProcessDefinitionId()).getMainProcess();
        //        Process mainProcess = repositoryService.getBpmnModel(execution.getProcessDefinitionId())
        //        .getMainProcess();
        UserTask userTask = (UserTask) mainProcess.getFlowElement(currentActivityId);

        // 从 version=1.2.1-SNAPSHOT 开始，才给 process 节点增加了 serverVersion 属性

        Optional<String> processServerVersion = getProcessServerVersion(mainProcess);
        if (processServerVersion.isPresent()) {
            Optional<BpmnFlowNodeType> nodeType = getNodeType(userTask);
            if (Objects.equals(FLOW_SERVER_VERSION_121, processServerVersion.get())
                    && nodeType.isPresent()
                    && (Objects.equals(NODE_TASK, nodeType.get()) || Objects.equals(NODE_BUSINESS, nodeType.get()))) {
                getApprovalMethod(userTask).ifPresent(method -> {

                    switch (method) {
                        case autoPassed:
                        case autoRejection:
                            // Do nothing
                            // TODO 不要调用查询审批人的接口，只需要保证该节点在实际运行过程中，能自动通过和拒绝即可
                            // 通过的功能可以参考下面枢智的方法，拒绝还需研究（可以结合 task 相关的事件去处理）
                            break;
                        default:
                            // 这里只会是 human 这一种情况， 因为 nobody 在转 BPMN 协议时，Activity 直接变成了 ReceiveTask 节点了。
                            List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
                            getApproverSpecify(userTask).ifPresent(specify -> {
                                assigners.addAll(approverSelect(specify.getType(), userTask, execution));
                            });

                            // 审批候选人为空时的兜底
                            emptyAssigneeHandle(assigners, userTask, execution);

                            List<String> assigneeIdList = new ArrayList<>();
                            for (BpmnTaskDelegateAssigner user : assigners) {
                                assigneeIdList.add(user.buildAssigneeId());
                            }
                            // UserTask 多实例, 该变量用于引擎
                            execution.setVariable(assigneeListVariableName, assigneeIdList);

                            execution.setVariableLocal(INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId,
                                    assigners);
                            break;
                    }
                });
            }
        } else {
            defaultCalcTaskAssigner(execution, userTask, currentActivityId, assigneeListVariableName);
        }

    }

    private void emptyAssigneeHandle(List<BpmnTaskDelegateAssigner> assigners, UserTask userTask,
                                     DelegateExecution execution) {
        // 审批人为空并且当前节点设置了自动跳过条件
        if (!CollectionUtils.isEmpty(assigners)) {
            return;
        }
        Optional<ApproverEmptyHandleTypeEnum> approverEmptyHandleType = getApproverEmptyHandleType(userTask);
        approverEmptyHandleType.ifPresent(type -> {
            switch (type) {
                case autoPassed:
                    // 自动通过的默认引擎参数必须设置为 true
                    execution.setTransientVariable(BpmnConstants.FLOWABLE_SKIP_EXPRESSION_ENABLE, true);
                    // 设置当前 UserTask 使用的 skip 表达式
                    execution.setTransientVariable(BPM_ALLOW_SKIP_USER_TASK, true);
                    break;
                case transferToAdmin:
                    approverSelect(ApproverEmptyHandleTypeEnum.transferToAdmin.getType(), userTask , execution);
                    break;
                default:
                    break;
            }
        });
    }

    /**
     * 根据审批人指定类型查询审批人
     *
     * @param type     "审批人指定"方式 {@link ApproverSpecifyEnum}
     * @param userTask 当前节点, 这个对象会包含配置元数据, 可以在该方法中或者基于 BpmnTaskAssigneeSelector 建一个抽象类, 做解析元数据公共方法
     * @return
     */
    private List<BpmnTaskDelegateAssigner> approverSelect(String type, UserTask userTask, DelegateExecution execution) {
        List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();

        // 如果开启了 mock 模式（workflow.mock=true）, 则直接返回 mock 的审批人
        if (Objects.nonNull(objectProvider.getIfAvailable())) {
            objectProvider.ifAvailable(selector -> assigners.addAll(selector.select(userTask, execution)));
        } else {
            selectors.forEach(select -> {
                if (select.support(type)) {
                    assigners.addAll(select.select(userTask, execution));
                }
            });
        }
        return assigners;
    }

    /**
     * 枢智版本的逻辑, 不太兼容了，暂时先放在这里
     *
     * @param execution
     * @param userTask
     * @param currentActivityId
     * @param assigneeListVariableName
     */
    @Deprecated
    private void defaultCalcTaskAssigner(DelegateExecution execution, UserTask userTask, String currentActivityId,
                                         String assigneeListVariableName) {

        BpmnTaskCalculateDTO calculateDTO = new BpmnTaskCalculateDTO();
        calculateDTO.setTaskId(userTask.getId());
        calculateDTO.setCategory(userTask.getCategory());
        calculateDTO.setProcessDefinitionId(execution.getProcessDefinitionId());
        calculateDTO.setProcessInstanceId(execution.getProcessInstanceId());
        calculateDTO.setTaskName(userTask.getName());
        calculateDTO.setTenantId(execution.getTenantId());
        calculateDTO.setTaskDefinitionKey(currentActivityId);
        calculateDTO.setVariables(execution.getVariables());

        bpmTaskDelegate.ifAvailable(delegate -> {
            List<BpmnTaskDelegateAssigner> assigners = delegate.calculateAssignerAtExecution(calculateDTO);
            List<String> assigneeIdList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(assigners)) {
                for (BpmnTaskDelegateAssigner user : assigners) {
                    assigneeIdList.add(user.buildAssigneeId());
                }
                // 审批人为空并且当前节点设置了自动跳过条件
            } else if (StringUtils.hasLength(userTask.getSkipExpression())) {
                // 自动通过的默认引擎参数必须设置为 true
                execution.setTransientVariable(BpmnConstants.FLOWABLE_SKIP_EXPRESSION_ENABLE, true);
                // 设置当前 UserTask 使用的 skip 表达式
                execution.setTransientVariable(BPM_ALLOW_SKIP_USER_TASK, true);
            }


            // UserTask 非多实例
            if (!userTask.hasMultiInstanceLoopCharacteristics()) {
                if (!CollectionUtils.isEmpty(assigneeIdList)) {
                    userTask.setAssignee(assigneeIdList.get(0));
                }
            } else {
                // UserTask 多实例, 该变量用于引擎
                execution.setVariable(assigneeListVariableName, assigneeIdList);
            }
            // 将当次审批节点下计算出来的人存储起来,方便后续对 task 保持审批人快照
            execution.setVariableLocal(INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId,
                    assigners);
        });
    }
}
