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

import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventProducer;
import cn.axzo.workflow.common.enums.ProcessTaskEventEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.response.mq.ProcessTaskDTO;
import cn.axzo.workflow.core.common.context.TaskOperationContext;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.listener.AbstractBpmnEventListener;
import cn.axzo.workflow.core.listener.BpmnTaskEventListener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Objects;

import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT;
import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION;
import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_ASSIGNED;
import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_COMPLETED;
import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_CREATED;
import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_DELETED;


/**
 * 流程任务的 MQ 发送监听
 *
 * @author wangli
 * @since 2023/7/10 18:22
 */
@Slf4j
@Component
public class RocketMqBpmnTaskEvent_102_Listener extends AbstractBpmnEventListener<TaskOperationContext> implements BpmnTaskEventListener, Ordered {
    @Override
    public int getOrder() {
        return Integer.MIN_VALUE + 102;
    }
    @Resource
    private EventProducer<?> eventProducer;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private RepositoryService repositoryService;
    @Value("${sendMq:true}")
    private Boolean sendMQ;

    @Override
    public void onAssigned(DelegateTask delegateTask) {
        log.info("RocketMqBpmnTaskEventListener#onAssigned...{}", delegateTask.getTaskDefinitionKey());
        if (StringUtils.hasLength(delegateTask.getAssignee()) && delegateTask.getAssignee().contains(TASK_ASSIGNEE_SKIP_FLAT)) {
            return;
        }
        ProcessTaskDTO dto = build(delegateTask, PROCESS_TASK_ASSIGNED);
        sendMessageQueue(dto, PROCESS_TASK_ASSIGNED);
        log.info("RocketMqBpmnTaskEventListener#onAssigned...end: {}", delegateTask.getTaskDefinitionKey());
    }

    @Override
    public void onCreated(DelegateTask delegateTask) {
        log.info("RocketMqBpmnTaskEventListener#onCreated...{}", delegateTask.getTaskDefinitionKey());
        ProcessTaskDTO dto = build(delegateTask, PROCESS_TASK_CREATED);
        sendMessageQueue(dto, PROCESS_TASK_CREATED);
        log.info("RocketMqBpmnTaskEventListener#onCreated...end: {}", delegateTask.getTaskDefinitionKey());
    }

    @Override
    public void onCompleted(DelegateTask delegateTask) {
        log.info("RocketMqBpmnTaskEventListener#onCompleted...{}", delegateTask.getTaskDefinitionKey());
        ProcessTaskDTO dto = build(delegateTask, PROCESS_TASK_COMPLETED);
        sendMessageQueue(dto, PROCESS_TASK_COMPLETED);
        log.info("RocketMqBpmnTaskEventListener#onCompleted...end: {}", delegateTask.getTaskDefinitionKey());
    }

    @Override
    public void onDeleted(DelegateTask delegateTask) {
        log.info("RocketMqBpmnTaskEventListener#onDeleted...{}", delegateTask.getTaskDefinitionKey());
        ProcessTaskDTO dto = build(delegateTask, PROCESS_TASK_DELETED);
        sendMessageQueue(dto, PROCESS_TASK_DELETED);
        log.info("RocketMqBpmnTaskEventListener#onDeleted...end: {}", delegateTask.getTaskDefinitionKey());
    }

    private Deployment getDeployment(String processInstanceId) {
        ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        return repositoryService.createDeploymentQuery().deploymentId(processInstance.getDeploymentId()).singleResult();
    }

    public ProcessTaskDTO build(DelegateTask delegateTask, ProcessTaskEventEnum type) {
        Process mainProcess = getContext().getProcess(() -> repositoryService.getBpmnModel(delegateTask.getProcessDefinitionId()).getMainProcess());
        ProcessTaskDTO dto = new ProcessTaskDTO()
                .setType(type)
                .setCategory(getDeployment(delegateTask.getProcessInstanceId()).getCategory())
                .setProcessTaskId(delegateTask.getId())
                .setProcessInstanceId(delegateTask.getProcessInstanceId())
                .setCurrentElementKey(delegateTask.getTaskDefinitionKey())
                .setCurrentElementName(delegateTask.getName())
                .setProcessDefinitionId(delegateTask.getProcessDefinitionId())
                .setInitiator(BpmnTaskDelegateAssigner.toObjectCompatible(delegateTask.getVariable(INTERNAL_INITIATOR)))
                .setApprover(BpmnTaskDelegateAssigner.toObjectCompatible(
                        delegateTask.getVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + delegateTask.getId())))
                .setVariables(delegateTask.getVariables())
                .setStartTime(delegateTask.getCreateTime())
                .setTenantId(delegateTask.getTenantId());
        BpmnMetaParserHelper.getNoticeConfig(mainProcess).ifPresent(dto::setNoticeConf);
        String version = (String) runtimeService.getVariable(delegateTask.getProcessInstanceId(),
                WORKFLOW_ENGINE_VERSION);
        if (Objects.isNull(version)) {
            version = FLOW_SERVER_VERSION_121;
        }
        dto.setWorkflowEngineVersion(version);
        return dto;
    }

    public void sendMessageQueue(ProcessTaskDTO dto, ProcessTaskEventEnum eventEnum) {
        if (!sendMQ) {
            return;
        }
        eventProducer.send(Event.builder()
                .shardingKey(dto.getProcessInstanceId())
                .eventCode(eventEnum.getEventCode())
                .targetId(dto.getProcessInstanceId())
                .targetType(eventEnum.getTag())
                .data(dto)
                .build());
    }


}
