package net.northking.cctp.executePlan.job;

import cn.hutool.core.collection.CollUtil;
import net.northking.cctp.executePlan.api.service.AtuPlanSceneCaseTaskApiService;
import net.northking.cctp.executePlan.api.service.AtuPlanTaskApiService;
import net.northking.cctp.executePlan.config.AtuPlanConfig;
import net.northking.cctp.executePlan.constants.PlanConstant;
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
import net.northking.cctp.executePlan.db.entity.AtuPlanTaskRecord;
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
import net.northking.cctp.executePlan.db.service.AtuPlanTaskRecordService;
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskExtendDto;
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecResultDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

@EnableScheduling
@Configuration
public class TaskExecTimeoutJob {

    private final static Logger logger = LoggerFactory.getLogger(TaskExecTimeoutJob.class);

    @Autowired
    private AtuPlanTaskApiService planTaskApiService;

    @Autowired
    private AtuPlanSceneCaseTaskService sceneCaseTaskService;

    @Autowired
    private AtuPlanSceneCaseTaskApiService sceneCaseTaskApiService;

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    private final static String TIMEOUT_LOCK = "TIMEOUT_LOCK";

    @Autowired
    private AtuPlanConfig atuPlanConfig;

    @Autowired
    private AtuPlanTaskRecordService atuPlanTaskRecordService;


    /**
     * 超时时间，默认3分钟，单位秒
     */
    public static final int TIMEOUT = 3 * 60;

    public static final String ERROR_MSG = "引擎执行任务超时";
    public static final String WAIT_TIMEOUT_ERROR_MSG = "任务等待超时，自动取消";

    @Scheduled(initialDelay = 10 * 1000, fixedDelay = 60 * 1000)
    private void handleTaskExecTimeout() {
        // 加锁，避免集群模式下未知异常
        Lock lock = redisLockRegistry.obtain(TIMEOUT_LOCK);
        boolean b = lock.tryLock();
        try {
            if (b) {
                // 查询正在执行中且心跳时间超过n分钟的任务
                List<AtuPlanTask> taskList = planTaskApiService.queryExecTimeoutTask(TIMEOUT);
                logger.debug("执行超时任务数量：{}", taskList.size());
                taskTimeoutHandle(taskList, PlanConstant.TASK_TIMEOUT_STATUS, ERROR_MSG);

                List<AtuPlanTask> waitTimeoutTask = planTaskApiService.queryWaitTimeoutTask(atuPlanConfig.getWaitTimeout());
                List<AtuPlanTaskRecord> reTryTasks = atuPlanTaskRecordService.queryReTryTaskByTaskIds(waitTimeoutTask.stream().map(AtuPlanTask::getId).collect(Collectors.toList()));
                List<String> reTryTaskIds = reTryTasks.stream().map(AtuPlanTaskRecord::getTaskId).collect(Collectors.toList());
                waitTimeoutTask.removeIf(task -> reTryTaskIds.contains(task.getId()));
                logger.debug("等待超时任务数量：{}", waitTimeoutTask.size());
                taskTimeoutHandle(waitTimeoutTask, PlanConstant.TASK_CANCEL_STATUS, WAIT_TIMEOUT_ERROR_MSG);

                List<AtuPlanSceneCaseTask> sceneCaseTaskList = sceneCaseTaskService.queryExecTimeoutTask(TIMEOUT);
                logger.debug("场景节点执行超时任务数量：{}", sceneCaseTaskList.size());
                sceneTaskTimeoutHandle(sceneCaseTaskList, PlanConstant.TASK_TIMEOUT_STATUS, ERROR_MSG);
                List<AtuPlanSceneCaseTask> sceneWaitTimeoutTask = sceneCaseTaskService.queryWaitTimeoutTask(atuPlanConfig.getWaitTimeout());
                List<AtuPlanTaskRecord> reTrySceneCaseTask = atuPlanTaskRecordService.queryReTryTaskByTaskIds(sceneWaitTimeoutTask.stream().map(AtuPlanSceneCaseTask::getTaskId).collect(Collectors.toList()));
                List<String> reTrySceneCaseTaskIds = reTrySceneCaseTask.stream().map(AtuPlanTaskRecord::getTaskId).collect(Collectors.toList());
                sceneWaitTimeoutTask.removeIf(sceneCaseTask -> reTrySceneCaseTaskIds.contains(sceneCaseTask.getId()));
                logger.debug("场景节点等待超时任务数量：{}", sceneWaitTimeoutTask.size());
                sceneTaskTimeoutHandle(sceneWaitTimeoutTask, PlanConstant.TASK_CANCEL_STATUS, WAIT_TIMEOUT_ERROR_MSG);
            }
        } finally {
            if (b) {
                lock.unlock();
            }
        }
    }

    private void sceneTaskTimeoutHandle(List<AtuPlanSceneCaseTask> sceneCaseTaskList, String status, String errorMsg) {
        if (CollUtil.isNotEmpty(sceneCaseTaskList)) {

            AtuTaskExecResultDto taskExecResult = new AtuTaskExecResultDto();
            taskExecResult.setCaseType(PlanConstant.SCRIPT_TYPE_SCENE);
            taskExecResult.setStatus(status);
            taskExecResult.setMessage(errorMsg);
            taskExecResult.setCurrentTime(System.currentTimeMillis());

            for (AtuPlanSceneCaseTask sceneCaseTask : sceneCaseTaskList) {
                AtuPlanTaskExtendDto taskExtendDto = planTaskApiService.queryTaskExtendById(sceneCaseTask.getTaskId());
                if (taskExtendDto == null) {
                    logger.error("任务[{}]信息不存在", sceneCaseTask.getTaskId());
                    logger.error("删除任务[{}]的节点任务信息", sceneCaseTask.getTaskId());
                    AtuPlanSceneCaseTask delParams = new AtuPlanSceneCaseTask();
                    delParams.setTaskId(sceneCaseTask.getTaskId());
                    sceneCaseTaskService.deleteByExample(delParams);
                    continue;
                }
                taskExecResult.setPlanId(taskExtendDto.getPlanId());
                taskExecResult.setBatchId(taskExtendDto.getBatchId());
                taskExecResult.setCaseId(taskExtendDto.getCaseId());
                taskExecResult.setTaskId(sceneCaseTask.getId());
                AtuPlanTask planTask = new AtuPlanTask();
                BeanUtils.copyProperties(taskExtendDto, planTask);
                // 场景节点执行失败，执行下一节点
                sceneCaseTaskApiService.queryNextNodeInfo(taskExecResult, sceneCaseTask, planTask);
            }
        }
    }

    private void taskTimeoutHandle(List<AtuPlanTask> taskList, String status, String errorMsg) {
        if (CollUtil.isNotEmpty(taskList)) {
            for (AtuPlanTask planTask : taskList) {
                planTaskApiService.taskExecFailUpdate(planTask, status, errorMsg);
            }
        }
    }
}
