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.AttachmentDTO;
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 cn.axzo.workflow.core.service.converter.BpmnHistoricAttachmentConverter;
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.TaskService;
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.context.annotation.Scope;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
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.MQ_OWNERSHIP_APPLICATION;
import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_PROCESS_DEFINITION_KEY;
import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_OWNERSHIP_APPLICATION;
import static cn.axzo.workflow.common.constant.BpmnConstants.SKIP_MQ;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ATTACHMENTS_VAR_NAME;
import static cn.axzo.workflow.common.constant.BpmnConstants.TRANSFER_TO;
import static cn.axzo.workflow.common.constant.BpmnConstants.TRANSFER_TO_ADVICE;
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;
import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_TRANSFER;


/**
 * 流程任务的 MQ 发送监听
 *
 * @author wangli
 * @since 2023/7/10 18:22
 */
@Slf4j
@Component
@Scope("prototype")
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 TaskService taskService;
    @Resource
    private BpmnHistoricAttachmentConverter attachmentConverter;
    @Resource
    private RepositoryService repositoryService;
    @Value("${sendMq:true}")
    private Boolean sendMQ;

    @Override
    public void onAssigned(DelegateTask delegateTask) {
        log.info("RocketMqBpmnTaskEventListener#onAssigned...{}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId());
        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: {}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId());
    }

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

    @Override
    public void onCompleted(DelegateTask delegateTask) {
        log.info("RocketMqBpmnTaskEventListener#onCompleted...{}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId());
        Object skipMq = delegateTask.getTransientVariable(SKIP_MQ);
        if (Objects.nonNull(skipMq) && Boolean.TRUE.equals(skipMq)) {
            // reject 使用了虚拟任务，会触发完成
            return;
        }
        ProcessTaskDTO dto = build(delegateTask, PROCESS_TASK_COMPLETED);
        sendMessageQueue(dto, PROCESS_TASK_COMPLETED);
        log.info("RocketMqBpmnTaskEventListener#onCompleted...end: {}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId());
    }

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

    @Override
    public void onTransfer(DelegateTask delegateTask) {
        log.info("RocketMqBpmnTaskEventListener#onTransfer...{}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId());
        BpmnTaskDelegateAssigner assigner = (BpmnTaskDelegateAssigner) delegateTask.getTransientVariable(TRANSFER_TO);
        String transferAdvice = (String) delegateTask.getTransientVariable(TRANSFER_TO_ADVICE);
        ProcessTaskDTO dto = build(delegateTask, PROCESS_TASK_TRANSFER);
        dto.setTransferTargetApprover(assigner);
        dto.setAdvice(transferAdvice);
        sendMessageQueue(dto, PROCESS_TASK_TRANSFER);
        log.info("RocketMqBpmnTaskEventListener#onTransfer...end: {}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId());
    }

    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());
        //1.获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(delegateTask.getProcessInstanceId()).singleResult();
        String category = getDeployment(delegateTask.getProcessInstanceId()).getKey();
        ProcessTaskDTO dto = new ProcessTaskDTO()
            .setType(type)
            .setCategory(category)
            .setProcessDefinitionKey(category)
            .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())
            .setBusinessKey(processInstance.getBusinessKey())
            .setAdvice(delegateTask.getTransientVariableLocal(COMMENT_TYPE_ADVICE) == null ? null : (String) delegateTask.getTransientVariableLocal(COMMENT_TYPE_ADVICE));
        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);
        Object attachmentObj = delegateTask.getTransientVariableLocal(TASK_ATTACHMENTS_VAR_NAME);
        if (attachmentObj != null) {
            dto.setAttachments((List<AttachmentDTO>) attachmentObj);
        }
        return dto;
    }

    public void sendMessageQueue(ProcessTaskDTO dto, ProcessTaskEventEnum eventEnum) {
        if (!sendMQ) {
            return;
        }
        Map<String, String> header = new HashMap<>();
        if (StringUtils.hasText(dto.getProcessDefinitionKey())) {
            if (log.isDebugEnabled()) {
                log.info("record process definition key: {}", dto.getProcessDefinitionKey());
            }
            header.put(MQ_OWNERSHIP_PROCESS_DEFINITION_KEY, dto.getProcessDefinitionKey());
        }
        if (dto.getVariables().containsKey(PROCESS_OWNERSHIP_APPLICATION)) {
            Object orDefault = dto.getVariables().getOrDefault(PROCESS_OWNERSHIP_APPLICATION, "");
            if (Objects.nonNull(orDefault)) {
                log.info("record process ownership app name: {}", orDefault);
                header.put(MQ_OWNERSHIP_APPLICATION, orDefault.toString());
            }
        }
        eventProducer.send(Event.builder()
            .shardingKey(dto.getProcessInstanceId())
            .eventCode(eventEnum.getEventCode())
            .targetId(dto.getProcessInstanceId())
            .targetType(dto.getProcessDefinitionKey())
            .data(dto)
            .build(), header);
    }


}
