package com.qqchen.deploy.backend.workflow.listener.flowable.execution;

import com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.dto.event.WorkflowNodeInstanceStatusChangeEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;

import java.time.LocalDateTime;
import java.util.Optional;

import static com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants.WORKFLOW_PREVIOUS_NODE_EXECUTION_STATE_VARIABLE_NAME;

@Slf4j
@Component("globalNodeExecutionListener")
public class GlobalNodeExecutionListener implements ExecutionListener {

    @Resource
    @Lazy
    private ApplicationEventPublisher eventPublisher;

    @Override
    public void notify(DelegateExecution execution) {
        // 获取当前节点信息
        FlowElement flowElement = execution.getCurrentFlowElement();
        String eventName = execution.getEventName();
        String processInstanceId = execution.getProcessInstanceId();
        String executionId = execution.getId();
        String nodeId = execution.getCurrentActivityId();
        String nodeName = flowElement.getName();
        String nodeType = flowElement.getClass().getSimpleName();
        log.debug("Node execution event: {}, processInstanceId: {}, nodeId: {}, nodeType: {}, nodeName:{}", eventName, processInstanceId, nodeId, nodeType, nodeName);
        LocalDateTime now = LocalDateTime.now();
        WorkflowNodeInstanceStatusEnums status = null;
        LocalDateTime startTime = null;
        LocalDateTime endTime = null;
        String nodeExecutionStatus = Optional.ofNullable(execution.getVariables())
            .map(vars -> vars.get(WORKFLOW_PREVIOUS_NODE_EXECUTION_STATE_VARIABLE_NAME))
            .map(Object::toString)
            .orElse(null);
        switch (eventName) {
            case ExecutionListener.EVENTNAME_START:
                status = WorkflowNodeInstanceStatusEnums.RUNNING;
                startTime = now;
                break;
            case ExecutionListener.EVENTNAME_END:
                if (StringUtils.isEmpty(nodeExecutionStatus)) {
                    status = WorkflowNodeInstanceStatusEnums.COMPLETED;
                } else {
                    if (WorkFlowConstants.WORKFLOW_NODE_EXECUTION_STATE_SUCCESS.equals(nodeExecutionStatus)) {
                        status = WorkflowNodeInstanceStatusEnums.COMPLETED;
                    } else {
                        status = WorkflowNodeInstanceStatusEnums.FAILED;
                    }
                }
                endTime = now;
                break;
            default:
                log.warn("Unexpected event type: {}", eventName);
                return;
        }
        eventPublisher.publishEvent(WorkflowNodeInstanceStatusChangeEvent.builder()
            .processInstanceId(processInstanceId)
            .executionId(executionId)
            .nodeId(nodeId)
            .nodeName(nodeName)
            .nodeType(nodeType)
            .status(status)
            .startTime(startTime)
            .endTime(endTime)
            .build());
    }
}