package com.cmvr.aima.listener;

import cn.hutool.core.util.StrUtil;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.framework.websocket.service.MessagePushService;
import com.cmvr.aima.domain.AimaTaskInstance;
import com.cmvr.aima.domain.AimaTestLog;
import com.cmvr.aima.enums.AimaTaskStatusEnum;
import com.cmvr.aima.service.IAimaTaskInstanceService;
import com.cmvr.aima.service.IAimaTestLogService;
import com.cmvr.test.flow.runtime.event.FlowExecutionEvent;
import com.cmvr.test.flow.runtime.event.FlowExecutionListener;
import com.cmvr.test.flow.runtime.event.FlowExecutionModuleCodes;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 爱玛测试工作流执行监听器
 * 监听流程引擎事件，自动记录测试日志和更新任务状态
 * 
 * @author cmvr-iot
 * @since 2026-06-22
 */
@Component
@Slf4j
public class AimaFlowExecutionListener implements FlowExecutionListener {
    private final IAimaTaskInstanceService aimaTaskInstanceService;
    private final IAimaTestLogService aimaTestLogService;
    private final MessagePushService messagePushService;

    /** key:itemId, value:已完成节点集合 */
    private final Map<String, Set<String>> itemNodeExecutMap = new HashMap<>();
    /** key:taskInsId(流程实例id), value:已执行item集合 */
    private final Map<String, Set<String>> taskItemExecutMap = new HashMap<>();

    public AimaFlowExecutionListener(IAimaTaskInstanceService aimaTaskInstanceService,
                                     IAimaTestLogService aimaTestLogService,
                                     MessagePushService messagePushService) {
        this.aimaTaskInstanceService = aimaTaskInstanceService;
        this.aimaTestLogService = aimaTestLogService;
        this.messagePushService = messagePushService;
    }

    @Override
    public String getModuleCode() {
        return FlowExecutionModuleCodes.AIMA;
    }

    @Override
    public void onEvent(FlowExecutionEvent event) {
        String instId = event.getInstId();
        String itemId = event.getItemId();
        String nodeId = event.getNodeId();
        if (StrUtil.isBlank(instId)) {
            return;
        }

        // 1.节点完成才记录当前item下node
        if (StrUtil.isNotBlank(itemId) && StrUtil.isNotBlank(nodeId)
                && FlowExecutionEvent.EventType.NODE_COMPLETED == event.getEventType()) {
            itemNodeExecutMap.computeIfAbsent(itemId, k -> new HashSet<>()).add(nodeId);
        }
        // 任意事件：当前任务绑定item（item一启动就入Map）
        if (StrUtil.isNotBlank(instId) && StrUtil.isNotBlank(itemId)) {
            taskItemExecutMap.computeIfAbsent(instId, k -> new HashSet<>()).add(itemId);
        }

        // 查询任务实例
        AimaTaskInstance taskInstance = aimaTaskInstanceService.lambdaQuery()
                .eq(AimaTaskInstance::getTaskInsId, instId)
                .one();
        if (taskInstance == null) {
            log.warn("未查询到爱玛测试实例,taskInsId:{}", instId);
            return;
        }

        int targetStatus = AimaTaskStatusEnum.RUNNING.getCode();
        boolean needUpdateDb = false;
        Date now = DateUtils.getNowDate();
        
        switch (event.getEventType()) {
            case TASK_COMPLETED:
                targetStatus = AimaTaskStatusEnum.SUCCESS.getCode();
                needUpdateDb = true;
                clearCache(instId);
                pushCompleteMsg(taskInstance.getId(), now, targetStatus);
                break;
            case TASK_FAILED:
                targetStatus = AimaTaskStatusEnum.FAILED.getCode();
                needUpdateDb = true;
                clearCache(instId);
                break;
            case NODE_COMPLETED:
            default:
                break;
        }

        if (needUpdateDb) {
            taskInstance.setStatus(targetStatus);
            taskInstance.setEndTime(now);
            aimaTaskInstanceService.updateById(taskInstance);
        }

        // 节点推送 - 记录日志
        if (StrUtil.isNotBlank(nodeId)) {
            int totalCases = taskInstance.getTotalCases() == null ? 0 : taskInstance.getTotalCases();
            double progress = 0D;
            if (totalCases > 0) {
                // 当前任务已执行item数量
                Set<String> itemSet = taskItemExecutMap.getOrDefault(instId, new HashSet<>());
                int itemTotal = itemSet.size();
                int finishedItemNum = itemTotal - 1;

                // 当前item已完成节点数
                Set<String> finishedNodeSet = itemNodeExecutMap.getOrDefault(itemId, new HashSet<>());
                int finishedNode = finishedNodeSet.size();
                int totalNode = event.getNodeCount() == 0 ? 1 : event.getNodeCount();

                // 已完整跑完用例占比
                double itemTotalRatio = finishedItemNum / (double) totalCases;
                // 当前运行item内部节点占比
                double innerRatio = finishedNode / (double) totalNode / totalCases;

                // 防止循环节点进度超限
                double singleItemBase = 1d / totalCases;
                innerRatio = Math.max(singleItemBase - 0.01, innerRatio);

                progress = (itemTotalRatio + innerRatio) * 100;
            }

            String info = event.getEventType() == FlowExecutionEvent.EventType.NODE_STARTED ? "开始执行" : "执行完成";
            String logId = UUID.randomUUID().toString().replace("-", "");
            // 四舍五入保留2位小数
            progress = Math.round(progress * 100) / 100.0;
            
            // 保存日志到数据库
            AimaTestLog testLog = AimaTestLog.builder()
                    .id(logId)
                    .taskInstanceId(taskInstance.getId())
                    .taskId(taskInstance.getTaskId())
                    .testCaseId(itemId)
                    .logLevel("INFO")
                    .logType("执行日志")
                    .logContent("节点【" + event.getNodeName() + "】" + info)
                    .logTime(now)
                    .status(targetStatus)
                    .executionStep(event.getNodeName())
                    .testStatus("0") // 0未执行
                    .durationMs(0L)
                    .progress( progress)
                    .build();

            try {
                aimaTestLogService.insertAimaTestLog(testLog);
                log.info("爱玛测试日志保存成功,logId:{},taskInstanceId:{},nodeId:{}", logId, taskInstance.getId(), nodeId);
            } catch (Exception e) {
                log.error("爱玛测试日志保存失败,taskInstanceId:{},nodeId:{}", taskInstance.getId(), nodeId, e);
            }
            
            try {
                messagePushService.pushToChannel("AimaTaskInstance", testLog);
            } catch (Exception e) {
                log.info("消息推送异常，无订阅忽略,itemId:{}", itemId, e);
            }
        }
    }

    /** 任务结束统一清理缓存 */
    private void clearCache(String taskInsId) {
        Set<String> itemList = taskItemExecutMap.getOrDefault(taskInsId, new HashSet<>());
        itemList.forEach(itemNodeExecutMap::remove);
        taskItemExecutMap.remove(taskInsId);
    }

    /**
     * 推送完成任务信息
     */
    public void pushCompleteMsg(String dbInsId, Date now, int targetStatus) {
        String logId = UUID.randomUUID().toString().replace("-", "");

        // 保存完成日志
        AimaTestLog testLog = AimaTestLog.builder()
                .id(logId)
                .taskInstanceId(dbInsId)
                .logLevel("INFO")
                .logType("系统日志")
                .logContent("任务执行完成")
                .logTime(now)
                .status(targetStatus)
                .build();

        try {
            aimaTestLogService.insertAimaTestLog(testLog);
            log.info("任务完成日志保存成功,logId:{},taskInstanceId:{}", logId, dbInsId);
        } catch (Exception e) {
            log.error("任务完成日志保存失败,taskInstanceId:{}", dbInsId, e);
        }
        try {
            messagePushService.pushToChannel("AimaTaskInstance", testLog);
        } catch (Exception e) {
            log.info("消息推送异常，无订阅忽略,insId:{}", dbInsId, e);
        }
    }
}
