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

import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventProducer;
import cn.axzo.workflow.common.enums.ProcessInstanceEventEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.response.mq.ProcessInstanceDTO;
import cn.axzo.workflow.core.listener.BpmnProcessEventListener;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.delegate.event.FlowableProcessStartedEvent;
import org.flowable.engine.delegate.event.impl.FlowableProcessCancelledEventImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.repository.Deployment;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.enums.ProcessInstanceEventEnum.PROCESS_INSTANCE_CANCELLED;
import static cn.axzo.workflow.common.enums.ProcessInstanceEventEnum.PROCESS_INSTANCE_COMPLETED;
import static cn.axzo.workflow.common.enums.ProcessInstanceEventEnum.PROCESS_INSTANCE_CREATED;
import static cn.axzo.workflow.common.enums.ProcessInstanceEventEnum.PROCESS_INSTANCE_REJECTED;
import static cn.axzo.workflow.common.enums.ProcessInstanceEventEnum.PROCESS_INSTANCE_STARTED;


/**
 * 流程实例的 MQ 发送监听
 *
 * @author wangli
 * @since 2023/7/10 18:22
 */
@Slf4j
@Component
public class RocketMqBpmnProcessEventListener implements BpmnProcessEventListener, Ordered {

    @Resource
    private EventProducer<?> eventProducer;
    @Resource
    private RepositoryService repositoryService;

    @Override
    public void onCreated(FlowableEngineEntityEvent event) {
        if (log.isDebugEnabled()) {
            log.debug("RocketMQ onCreated: ClassName: {}, event: {}", event.getClass().getName(),
                    JSON.toJSONString(event));
        }
        Deployment deployment =
                repositoryService.createDeploymentQuery().deploymentId(((ExecutionEntityImpl) event.getEntity()).getDeploymentId()).singleResult();
        ProcessInstanceDTO dto = new ProcessInstanceDTO()
                .setType(PROCESS_INSTANCE_CREATED)
                .setCategory(deployment.getCategory())
                .setProcessInstanceId(event.getProcessInstanceId())
                .setCurrentElementKey(((ExecutionEntityImpl) event.getEntity()).getStartActivityId())
                .setProcessDefinitionId(event.getProcessDefinitionId())
                .setProcessDefinitionKey(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionKey())
                .setProcessDefinitionVersion(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionVersion())
                .setInitiator(((ExecutionEntityImpl) event.getEntity()).getVariable(INTERNAL_INITIATOR,
                        BpmnTaskDelegateAssigner.class))
                .setVariables(((ExecutionEntityImpl) event.getEntity()).getVariables())
                .setStartTime(((ExecutionEntityImpl) event.getEntity()).getStartTime())
                .setTenantId(((ExecutionEntityImpl) event.getEntity()).getTenantId())
                .setBusinessKey(((ExecutionEntityImpl) event.getEntity()).getBusinessKey())
                .setBusinessProcessInstanceName(((ExecutionEntityImpl) event.getEntity()).getName());
        log.info("RocketMqBpmProcessEventListener#onCreated MQ.Data: {}", JSON.toJSONString(dto));
        sendMessageQueue(dto, PROCESS_INSTANCE_CREATED);
    }

    @Override
    public void onStarted(FlowableProcessStartedEvent event) {
        if (log.isDebugEnabled()) {
            log.debug("RocketMQ onStarted: ClassName: {}, event: {}", event.getClass().getName(),
                    JSON.toJSONString(event));
        }
        Deployment deployment =
                repositoryService.createDeploymentQuery().deploymentId(((ExecutionEntityImpl) event.getEntity()).getDeploymentId())
                        .singleResult();
        ProcessInstanceDTO dto = new ProcessInstanceDTO()
                .setType(PROCESS_INSTANCE_STARTED)
                .setCategory(deployment.getCategory())
                .setProcessInstanceId(((ExecutionEntityImpl) event.getEntity()).getProcessInstanceId())
                .setCurrentElementKey(((ExecutionEntityImpl) event.getEntity()).getCurrentActivityId())
                .setProcessDefinitionId(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionId())
                .setProcessDefinitionKey(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionKey())
                .setProcessDefinitionVersion(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionVersion())
                .setInitiator(((ExecutionEntityImpl) event.getEntity()).getVariable(INTERNAL_INITIATOR,
                        BpmnTaskDelegateAssigner.class))
                .setVariables(((ExecutionEntityImpl) event.getEntity()).getVariables())
                .setStartTime(((ExecutionEntityImpl) event.getEntity()).getStartTime())
                .setTenantId(((ExecutionEntityImpl) event.getEntity()).getTenantId())
                .setBusinessKey(((ExecutionEntityImpl) event.getEntity()).getProcessInstance().getBusinessKey())
                .setBusinessProcessInstanceName(((ExecutionEntityImpl) event.getEntity()).getProcessInstance().getName());
        log.info("RocketMqBpmProcessEventListener#onStarted MQ.Data: {}", JSON.toJSONString(dto));
        sendMessageQueue(dto, PROCESS_INSTANCE_STARTED);
    }

    @Override
    public void onCancelled(FlowableCancelledEvent event) {
        if (log.isDebugEnabled()) {
            log.info("RocketMQ onCancelled: ClassName: {}, event: {}", event.getClass().getName(),
                    JSON.toJSONString(event));
        }
        Deployment deployment =
                repositoryService.createDeploymentQuery().deploymentId(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getDeploymentId())
                        .singleResult();
        ProcessInstanceDTO dto = new ProcessInstanceDTO()
                .setType(PROCESS_INSTANCE_CANCELLED)
                .setCategory(deployment.getCategory())
                .setProcessInstanceId(event.getProcessInstanceId())
                .setCurrentElementKey("取消流程实例无节点信息")
                .setProcessDefinitionId(event.getProcessDefinitionId())
                .setProcessDefinitionKey(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getProcessDefinitionKey())
                .setProcessDefinitionVersion(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getProcessDefinitionVersion())
                .setInitiator(((FlowableProcessCancelledEventImpl) event).getExecution().getVariable(INTERNAL_INITIATOR, BpmnTaskDelegateAssigner.class))
                .setVariables(((FlowableProcessCancelledEventImpl) event).getExecution().getVariables())
                .setStartTime(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getStartTime())
                .setTenantId(((FlowableProcessCancelledEventImpl) event).getExecution().getTenantId())
                .setBusinessKey(((FlowableProcessCancelledEventImpl) event).getExecution().getProcessInstanceBusinessKey())
                .setBusinessProcessInstanceName(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getName())
                .setCancelReason(String.valueOf(event.getCause()));
        log.info("RocketMqBpmProcessEventListener#onCancelled MQ.Data: {}", JSON.toJSONString(dto));
        sendMessageQueue(dto, PROCESS_INSTANCE_CANCELLED);
    }

    /**
     * 流程实例被拒绝后回调
     *
     * @param event
     */
    @Override
    public void onRejected(FlowableCancelledEvent event) {
        if (log.isDebugEnabled()) {
            log.info("RocketMQ onRejected: ClassName: {}, event: {}", event.getClass().getName(),
                    JSON.toJSONString(event));
        }
        Deployment deployment =
                repositoryService.createDeploymentQuery().deploymentId(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getDeploymentId())
                        .singleResult();
        ProcessInstanceDTO dto = new ProcessInstanceDTO()
                .setType(PROCESS_INSTANCE_REJECTED)
                .setCategory(deployment.getCategory())
                .setProcessInstanceId(event.getProcessInstanceId())
                .setCurrentElementKey("取消流程实例无节点信息")
                .setProcessDefinitionId(event.getProcessDefinitionId())
                .setProcessDefinitionKey(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getProcessDefinitionKey())
                .setProcessDefinitionVersion(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getProcessDefinitionVersion())
                .setInitiator(((FlowableProcessCancelledEventImpl) event).getExecution().getVariable(INTERNAL_INITIATOR, BpmnTaskDelegateAssigner.class))
                .setVariables(((FlowableProcessCancelledEventImpl) event).getExecution().getVariables())
                .setStartTime(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getStartTime())
                .setTenantId(((FlowableProcessCancelledEventImpl) event).getExecution().getTenantId())
                .setBusinessKey(((FlowableProcessCancelledEventImpl) event).getExecution().getProcessInstanceBusinessKey())
                .setBusinessProcessInstanceName(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getName())
                .setCancelReason(String.valueOf(event.getCause()));
        log.info("RocketMqBpmProcessEventListener#onRejected MQ.Data: {}", JSON.toJSONString(dto));
        sendMessageQueue(dto, PROCESS_INSTANCE_REJECTED);
    }

    @Override
    public void onCompleted(FlowableEngineEntityEvent event) {
        if (log.isDebugEnabled()) {
            log.info("RocketMQ onCompleted: ClassName: {}, event: {}", event.getClass().getName(),
                    JSON.toJSONString(event));
        }
        Deployment deployment =
                repositoryService.createDeploymentQuery().deploymentId(((ExecutionEntityImpl) event.getEntity()).getDeploymentId())
                        .singleResult();
        ProcessInstanceDTO dto = new ProcessInstanceDTO()
                .setType(PROCESS_INSTANCE_COMPLETED)
                .setCategory(deployment.getCategory())
                .setProcessInstanceId(event.getProcessInstanceId())
                .setCurrentElementKey("取消流程实例无节点信息")
                .setProcessDefinitionId(event.getProcessDefinitionId())
                .setProcessDefinitionKey(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionKey())
                .setProcessDefinitionVersion(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionVersion())
                .setInitiator(((ExecutionEntityImpl) event.getEntity()).getVariable(INTERNAL_INITIATOR,
                        BpmnTaskDelegateAssigner.class))
                .setVariables(((ExecutionEntityImpl) event.getEntity()).getVariables())
                .setStartTime(((ExecutionEntityImpl) event.getEntity()).getStartTime())
                .setTenantId(((ExecutionEntityImpl) event.getEntity()).getTenantId())
                .setBusinessKey(((ExecutionEntityImpl) event.getEntity()).getProcessInstanceBusinessKey())
                .setBusinessProcessInstanceName(((ExecutionEntityImpl) event.getEntity()).getName());
        log.info("RocketMqBpmProcessEventListener#onCompleted MQ.Data: {}", JSON.toJSONString(dto));
        sendMessageQueue(dto, PROCESS_INSTANCE_COMPLETED);
    }

    private void sendMessageQueue(ProcessInstanceDTO dto, ProcessInstanceEventEnum eventEnum) {
        eventProducer.send(Event.builder()
                .shardingKey(dto.getProcessInstanceId())
                .eventCode(eventEnum.getEventCode())
                .targetId(dto.getProcessInstanceId())
                .targetType(eventEnum.getTag())
                .data(dto)
                .build());
    }

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