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

import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.engine.event.MessagePushEvent;
import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder;
import cn.axzo.workflow.core.engine.job.AsyncApproveTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncRejectTaskJobHandler;
import cn.axzo.workflow.core.listener.BpmnAsyncJobEventListener;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.common.engine.impl.event.FlowableEntityEventImpl;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.job.api.JobInfo;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_HAS_BEEN_COMPLETE;
import static cn.axzo.workflow.core.listener.AbstractBpmnEventListener.parseProcessDefinitionKey;
import static org.flowable.common.engine.api.delegate.event.FlowableEngineEventType.JOB_MOVED_TO_DEADLETTER;

/**
 * 处理异步审批相关异常
 */
@Slf4j
@Component
@Scope("prototype")
public class ApproveErrorReporterEventListener implements BpmnAsyncJobEventListener {

    private final List<String> IGNORE_ERROR_CODES = Arrays.asList(
            TASK_HAS_BEEN_COMPLETE.getRespCode(),
            TASK_COMPLETE_FAIL_NOT_EXISTS.getRespCode(),
            TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF.getRespCode());

    private final List<String> RESOLVE_HANDLER_TYPES = Arrays.asList(
            AsyncApproveTaskJobHandler.TYPE,
            AsyncRejectTaskJobHandler.TYPE);

    @Override
    public boolean support(FlowableEngineEventType eventType) {
        return Objects.equals(JOB_MOVED_TO_DEADLETTER, eventType);
    }

    @Override
    public void notify(FlowableEvent flowableEvent) {
        log.info("ApproveErrorReporterEventListener-flowableEvent: {}", JSONUtil.toJsonStr(flowableEvent));
        if (!(flowableEvent instanceof FlowableEntityEventImpl)) {
            return;
        }
        FlowableEntityEventImpl jobEvent = (FlowableEntityEventImpl) flowableEvent;
        JobInfo jobInfo = (JobInfo) jobEvent.getEntity();
        String handlerType = jobInfo.getJobHandlerType();
        //只处理审批类型任务
        if (!RESOLVE_HANDLER_TYPES.contains(handlerType)) {
            log.warn("非审批类型任务,handlerType:{},event:{}", handlerType, JSONUtil.toJsonStr(flowableEvent));
            return;
        }
        if (StringUtils.isBlank(jobInfo.getCustomValues())) {
            log.warn("审批类型错误,CustomValues内容为空,event:{}", jobEvent);
            return;
        }

        BpmnTaskAuditDTO dto = JSONUtil.toBean(jobInfo.getCustomValues(), BpmnTaskAuditDTO.class);
        Process process = ProcessDefinitionUtil.getProcess(jobEvent.getProcessDefinitionId());
        //获取消息配置
        Optional<BpmnNoticeConf> noticeConfig = BpmnMetaParserHelper.getNoticeConfig(process);
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
        MessagePushEvent event = MessagePushEventBuilder.createPendingRollbackEvent(jobEvent.getProcessInstanceId(),
                parseProcessDefinitionKey(jobEvent.getProcessDefinitionId()),
                jobInfo.getTenantId(), dto.getTaskId(), noticeConfig.orElse(new BpmnNoticeConf()));
        log.info("发送恢复待办的流程事件消息: taskDto:{},event:{}", JSONUtil.toJsonStr(dto), JSONUtil.toJsonStr(event));
        eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey());
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }
}
