package cn.axzo.workflow.server.controller.listener.task;

import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.bpmn.task.ExtHiTaskSearchDTO;
import cn.axzo.workflow.core.common.context.TaskOperationContext;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskCmd;
import cn.axzo.workflow.core.engine.job.AsyncApproveTaskJobHandler;
import cn.axzo.workflow.core.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.listener.AbstractBpmnEventListener;
import cn.axzo.workflow.core.listener.BpmnTaskEventListener;
import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.job.service.JobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_DELETE_PROCESS_FLAG;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_TENANT_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_NAME;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_REJECT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
import static cn.axzo.workflow.core.common.enums.BpmnProcessTaskResultEnum.REJECTION_AUTO_COMPLETED;

/**
 * UserTask 节点自动通过和驳回处理
 *
 * @author wangli
 * @since 2023/11/21 11:30
 */
@Slf4j
@Component
@AllArgsConstructor
public class AutoOperatorEvent_101_Listener extends AbstractBpmnEventListener<TaskOperationContext> implements BpmnTaskEventListener, Ordered {
    @Override
    public int getOrder() {
        return Integer.MIN_VALUE + 101;
    }

    private final TaskService taskService;
    private final RuntimeService runtimeService;
    private final RepositoryService repositoryService;
    private final HistoryService historyService;
    private final ExtAxHiTaskInstService extAxHiTaskInstService;

    @Override
    public void onCreated(DelegateTask delegateTask) {
        log.info("AutoOperatorEventListener#onCreated...{}", delegateTask.getTaskDefinitionKey());

        if (Objects.equals(NODE_STARTER.getType(), delegateTask.getTaskDefinitionKey())) {
            createAsyncApproveJob(delegateTask);
            /*BpmnTaskDelegateAssigner initiator = getContext().getInitiator(() -> BpmnTaskDelegateAssigner.toObjectCompatible(delegateTask.getVariable(INTERNAL_INITIATOR)));
            if (Objects.nonNull(initiator)) {
                delegateTask.setVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + delegateTask.getId(),
                        initiator.toJson());
            }

            delegateTask.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + delegateTask.getId(),
                    APPROVED.getStatus());
            // 直接完成
            taskService.complete(delegateTask.getId(), runtimeService.getVariables(delegateTask.getExecutionId()));*/

            return;
        }

        Process mainProcess = repositoryService.getBpmnModel(delegateTask.getProcessDefinitionId()).getMainProcess();
        UserTask userTask = (UserTask) mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey());

        boolean exists = checkApproverExists(delegateTask, userTask, mainProcess);
        if (exists) {
            taskService.addComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), COMMENT_TYPE_ADVICE,
                    "同一审批人，自动过审");
            autoPass(delegateTask);
        }

        // 检测节点自身配置是否有自动操作
        checkApprovalMethod(delegateTask, userTask);

        log.info("AutoOperatorEventListener#onCreated...end: {}", delegateTask.getTaskDefinitionKey());
    }

    private void createAsyncApproveJob(DelegateTask delegateTask) {
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();

        JobEntity job = jobService.createJob();
        // 这里的 executionId 可为 null
        job.setExecutionId(delegateTask.getExecutionId());
        job.setProcessInstanceId(delegateTask.getProcessInstanceId());
        job.setProcessDefinitionId(delegateTask.getProcessDefinitionId());
        job.setElementId(delegateTask.getTaskDefinitionKey());
        job.setElementName(delegateTask.getName());
        job.setJobHandlerType(AsyncApproveTaskJobHandler.TYPE);
        job.setTenantId(delegateTask.getTenantId());

        BpmnTaskAuditDTO dto = new BpmnTaskAuditDTO();
        dto.setTaskId(delegateTask.getId());

        BpmnTaskDelegateAssigner initiator = getContext().getInitiator(() -> BpmnTaskDelegateAssigner.toObjectCompatible(delegateTask.getVariable(INTERNAL_INITIATOR)));
        if (Objects.nonNull(initiator)) {
            delegateTask.setVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + delegateTask.getId(),
                    initiator.toJson());
        }
        dto.setApprover(initiator);
        // 携带自定义的数据
        job.setCustomValues(JSONUtil.toJsonStr(dto));

        // 创建异步任务并调度
        jobService.createAsyncJob(job, false);
        jobService.scheduleAsyncJob(job);
    }

    /**
     * 校验当前的审批人是否存在过前一个节点
     *
     * @param delegateTask
     * @param userTask
     * @param mainProcess
     */
    private boolean checkApproverExists(DelegateTask delegateTask, UserTask userTask, Process mainProcess) {
        AtomicBoolean exists = new AtomicBoolean(false);
        FlowElement currentFlowElement = mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey());
        BpmnFlowNodeType currentNodeType = BpmnMetaParserHelper.getNodeType(currentFlowElement).orElse(NODE_EMPTY);
        if (!Objects.equals(currentNodeType, NODE_TASK)) {
            return exists.get();
        }
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        processEngineConfiguration.getActivityInstanceEntityManager()
                .findActivityInstancesByProcessInstanceId(delegateTask.getProcessInstanceId(), false)
                .stream()
                .filter(i -> !Objects.equals(i.getActivityId(), userTask.getId()))
                .filter(i -> !Objects.equals(i.getActivityType(), "exclusiveGateway"))
                .filter(i -> !Objects.equals(i.getActivityType(), "sequenceFlow"))
                .max(Comparator.comparing(ActivityInstanceEntity::getStartTime))
                .ifPresent(i -> {
                    // 与发起人比对
                    if (Objects.equals(NODE_STARTER.getType(), i.getActivityId())) {
                        BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(delegateTask.getVariable(INTERNAL_INITIATOR));
                        if (Objects.nonNull(initiator) && initiator.comparePersonIdToOther(delegateTask.getAssignee())) {
                            exists.compareAndSet(false, true);
                        }
                    } else {
                        FlowElement flowElement = mainProcess.getFlowElement(i.getActivityId());
                        BpmnMetaParserHelper.getNodeType(flowElement).ifPresent(j -> {
                            if (Objects.equals(NODE_TASK, j)) {
                                ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
                                searchDTO.setProcessInstanceId(delegateTask.getProcessInstanceId());
                                searchDTO.setTaskDefinitionKey(i.getActivityId());
                                getContext().getExtAxHiTaskInsts(() -> extAxHiTaskInstService.queryList(searchDTO))
                                        .stream().filter(e -> Objects.equals(e.getStatus(), APPROVED.getStatus()))
                                        .map(ExtAxHiTaskInst::getAssignee)
                                        .filter(Objects::nonNull)
                                        .filter(StringUtils::hasText)
                                        .filter(k -> specialApproverComparison(k, delegateTask.getAssignee()))
                                        .findAny().ifPresent(k -> exists.compareAndSet(false, true));
                            }
                        });
                    }
                });
        return exists.get();
    }

    /**
     * 特殊审批人字段的比对, 兼容旧迭代导致的数据格式
     *
     * @return
     */
    private boolean specialApproverComparison(String compareAssignee, String currentAssignee) {
        if (StringUtils.hasText(compareAssignee) && StringUtils.hasText(currentAssignee)) {
            String[] compareSplit = compareAssignee.split("\\|");
            String[] currentSplit = currentAssignee.split("\\|");
            if (compareSplit.length == 2 || currentSplit.length == 2) {
                return Objects.equals(compareSplit[1], currentSplit[1]);
            }
        }
        return false;
    }

    /**
     * 如果审批人为空时, 读取 approverEmptyHandleType = 自动通过或自动驳回
     *
     * @param delegateTask
     * @param userTask
     */
    private void checkApproverEmptyHandle(DelegateTask delegateTask, UserTask userTask) {
        if (!StringUtils.hasText(delegateTask.getAssignee())) {
            BpmnMetaParserHelper.getApproverEmptyHandleType(userTask)
                    .ifPresent(approverEmptyHandleTypeEnum -> {
                        switch (approverEmptyHandleTypeEnum) {
                            case autoPassed:
                                autoPass(delegateTask);
                                break;
                            case autoRejection:
                                autoReject(delegateTask);
                                break;
                            case autoSkipped:
                                // autoReject(delegateTask);
                                // 非产品需求, 暂时不实现, 这里的功能似乎可以用 taskService.deleteTask 来实现
                                break;
                            default:
                                break;
                        }
                    });
        }
    }

    /**
     * 如果 approverMethod = 自动通过或自动驳回时
     *
     * @param delegateTask
     * @param userTask
     */
    private void checkApprovalMethod(DelegateTask delegateTask, UserTask userTask) {
        BpmnMetaParserHelper.getApprovalMethod(userTask)
                .ifPresent(approvalMethodEnum -> {
                    switch (approvalMethodEnum) {
                        case autoPassed:
                            autoPass(delegateTask);
                            break;
                        case autoRejection:
                            autoReject(delegateTask);
                            break;
                        case bizSpecify:
                            break;
                        default:
                            checkApproverEmptyHandle(delegateTask, userTask);
                            break;
                    }
                });
    }

    private void autoReject(DelegateTask delegateTask) {
        log.info("AutoOperatorEventListener#autoReject...{}", delegateTask.getTaskDefinitionKey());
        taskService.addComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), COMMENT_TYPE_OPERATION_DESC
                , "自动驳回");
        delegateTask.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + delegateTask.getId(), REJECTED.getStatus());
        Map<String, Object> variables = new HashMap<>();
        variables.put(INTERNAL_END_TENANT_ID, delegateTask.getTenantId());
        variables.put(INTERNAL_END_USER_NAME, "系统");
        variables.put(INTERNAL_END_USER_ID, "system");
        variables.put(INTERNAL_DELETE_PROCESS_FLAG, INTERNAL_PROCESS_TYPE_REJECT);
        runtimeService.setVariables(delegateTask.getProcessInstanceId(), variables);

        // FIXME 这里的删除流程实例, 应该利用 DeleteProcessInstanceOperation 实现
//        List<HistoricTaskInstance> historyTaskList =
//                historyService.createHistoricTaskInstanceQuery()
//                        .processInstanceId(delegateTask.getProcessInstanceId())
//                        .unfinished()
//                        .list();
//        if (!CollectionUtils.isEmpty(historyTaskList)) {
//            historyTaskList.stream().filter(i -> Objects.isNull(i.getExecutionId()))
//                    .forEach(i -> taskService.complete(i.getId()));
//        }
//        // 删除流程实例，以实现驳回任务时，中止整个审批流程
//        runtimeService.deleteProcessInstance(delegateTask.getProcessInstanceId(), REJECTION_AUTO_COMPLETED.getDesc());
        CommandContext commandContext = CommandContextUtil.getCommandContext();
        CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext,
                delegateTask.getProcessInstanceId(), extAxHiTaskInstService, REJECTION_AUTO_COMPLETED.getDesc()));
        log.info("AutoOperatorEventListener#autoReject...end: {}", delegateTask.getTaskDefinitionKey());
    }


    private void autoPass(DelegateTask delegateTask) {
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
        BpmnTaskDelegateAssigner assigner = BpmnTaskDelegateAssigner.toObjectCompatible(
                delegateTask.getVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + delegateTask.getId()));
        commandExecutor.execute(new CustomApproveTaskCmd(delegateTask.getId(), null, "自动通过",
                Collections.emptyList(), assigner, null));
    }

}
