package net.northking.cctp.executePlan.consumer;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import net.northking.cctp.executePlan.api.service.AtuPlanBatchApiService;
import net.northking.cctp.executePlan.api.service.AtuPlanSceneCaseTaskApiService;
import net.northking.cctp.executePlan.constants.PlanConstant;
import net.northking.cctp.executePlan.constants.RabbitConstant;
import net.northking.cctp.executePlan.constants.RedisConstant;
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
import net.northking.cctp.executePlan.db.entity.AtuPlanTaskRecord;
import net.northking.cctp.executePlan.db.service.AtuPlanInfoService;
import net.northking.cctp.executePlan.db.service.AtuPlanTaskRecordService;
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecResultDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.concurrent.CompletableFuture;

/**
 * <p>Title: TaskExecResultConsumer</p>
 * <p>Description: 任务执行结果</p>
 *
 * @Author chenjianfeng
 * @Version V1.0
 * @Date 2022/12/12
 * <p>Copyright (c) 京北方信息技术股份有限公司 Corporation 2022 . All rights reserved.</p>
 */
@Component
public class TaskExecResultConsumer {

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

    @Autowired
    private AtuPlanTaskService planTaskService;

    @Autowired
    private AtuPlanInfoService atuPlanInfoService;

    @Autowired
    private AmqpAdmin amqpAdmin;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private AtuPlanBatchApiService planBatchApiService;

    @Autowired
    private AtuPlanSceneCaseTaskApiService sceneCaseTaskApiService;

    @Autowired
    private AtuPlanTaskRecordService taskRecordService;

    @PostConstruct
    public void init(){
        // 初始化队列并绑定到交换机
        Exchange exchange = new DirectExchange(RabbitConstant.PROJECT_EXCHANGE_KEY);
        amqpAdmin.declareExchange(exchange);
        Queue queue = new Queue(RabbitConstant.TASK_EXEC_RESULT);
        amqpAdmin.declareQueue(queue);
        Binding binding = BindingBuilder.bind(queue).to(exchange)
                .with(RabbitConstant.TASK_EXEC_RESULT).noargs();
        amqpAdmin.declareBinding(binding);
    }


    /**
     * 接收任务执行结果
     * @param message 对象消息
     */
    @Transactional(rollbackFor = Exception.class)
    @RabbitListener(queues = RabbitConstant.TASK_EXEC_RESULT)
    public void receive(Message message){
        try{
            logger.info("任务执行结果 ==> " + new String(message.getBody()));
            JSONObject event = JSONUtil.parseObj(new String(message.getBody()));
            AtuTaskExecResultDto taskExecResult = JSONUtil.toBean(event, AtuTaskExecResultDto.class);

            String clusterKeyPrefix = RedisConstant.CLUSTER_KEY_PREFIX + taskExecResult.getBatchId().substring(0,4) +
                        RedisConstant.CLUSTER_KEY_SUFFIX;

            if (ObjectUtil.equal(PlanConstant.TASK_ASSERT_FAIL_STATUS, taskExecResult.getStatus())){
                // 断言失败设置为执行失败
                taskExecResult.setStatus(PlanConstant.TASK_EXECUTE_FAIL_STATUS);
            }
            // 判断是否场景用例
            if (ObjectUtil.equal(PlanConstant.SCRIPT_TYPE_SCENE, taskExecResult.getCaseType())){
                sceneCaseTaskApiService.sceneCaseHandle(taskExecResult, clusterKeyPrefix);
                return;
            }
            // 查询任务信息
            AtuPlanTask planTask = planTaskService.findByPrimaryKey(taskExecResult.getTaskId());
            if (ObjectUtil.isNull(planTask)){
                logger.error("任务[" + taskExecResult.getTaskId() + "]信息不存在");
                return;
            }
            //判断任务状态是否未非引擎返回的状态 取消/超时
            if(ObjectUtil.equal(PlanConstant.TASK_CANCEL_STATUS, planTask.getStatus())
                    ||ObjectUtil.equal(PlanConstant.TASK_TIMEOUT_STATUS, planTask.getStatus())){
                return;
            }
            // 判断是否开始执行
            if (ObjectUtil.equal(PlanConstant.TASK_START_EXECUTE_STATUS, taskExecResult.getStatus())){
                planTask.setStartTime(taskExecResult.getCurrentTime());
                planTask.setEngineId(taskExecResult.getEngineId());
                planTask.setDeviceId(taskExecResult.getDeviceId());
                planTask.setAppId(taskExecResult.getAppId());
                planTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
                planTask.setLastHeartbeatTime(new Date());

                if (!redisTemplate.opsForHash().hasKey(clusterKeyPrefix + RedisConstant.BATCH_SCRIPT_SUM_KEY + planTask.getBatchId(),
                        clusterKeyPrefix + PlanConstant.BATCH_START_TIME)) {
                    logger.debug("批次[" + planTask.getBatchId() + "]开始执行，更新计划的状态");
                    atuPlanInfoService.updatePlanByLastBatchId(planTask.getBatchId(), PlanConstant.PLAN_EXECUTING_STATUS);
                }

            }else if(taskExecResult.isNeedRetry()) {    // 任务需要失败后需要重试
                // 添加任务执行记录
                addTaskRecord(taskExecResult, planTask);

                // 任务重试，结束时间作为重试的开始时间
                AtuPlanTask updateTaskTime = new AtuPlanTask();
                updateTaskTime.setId(planTask.getId());
                updateTaskTime.setStartTime(taskExecResult.getCurrentTime());
                planTaskService.updateByPrimaryKey(updateTaskTime);
                // 结束
                return;
            }else{
                planTask.setStatus(taskExecResult.getStatus());
                planTask.setEndTime(taskExecResult.getCurrentTime());
                planTask.setErrorMsg(taskExecResult.getMessage());
                planTask.setVideoUrl(taskExecResult.getVideoPath());
                planTask.setExecResultFile(taskExecResult.getFilePath());
                // 任务完成后查询数据后将数据插入到表atu_plan_task_record
                addTaskRecord(taskExecResult, planTask);
            }

            if (PlanConstant.SCRIPT_TYPE_ANDROID.equals(planTask.getCaseType())
                    || PlanConstant.SCRIPT_TYPE_IOS.equals(planTask.getCaseType())) {
                logger.debug("处理移动端性能文件数据");
                planTask.setPerDataPath(sceneCaseTaskApiService.parseMobilePerformanceFile(taskExecResult));
            }

            // 终态休眠一会，避免集群同时更新执行中状态造成数据异常
            if (!PlanConstant.TASK_START_EXECUTE_STATUS.equals(planTask.getStatus())){
                try {
                    Thread.sleep(5000);
                }catch (Exception e){
                    logger.debug("");
                }
            }
            planTaskService.updateByPrimaryKey(planTask);

            // 更新批次统计数据
            planBatchApiService.updateCacheBatchSumData(planTask);

            // 关联截图文件
            CompletableFuture.runAsync(() -> sceneCaseTaskApiService.associatedActualImgAndVideo(taskExecResult));
        }catch (Exception e){
            logger.error("任务结果更新失败", e);
        }
    }

    private void addTaskRecord(AtuTaskExecResultDto taskExecResult, AtuPlanTask planTask) {
        try {
            planTask.setEndTime(taskExecResult.getCurrentTime());

            AtuPlanTaskRecord record = new AtuPlanTaskRecord();
            record.setTaskId(planTask.getId());
            // 根据taskId查询执行序号
            long count = taskRecordService.count(record);
            if (count > 0) {
                record.setExecuteType(PlanConstant.TASK_EXECUTE_TYPE_AUTO_RETRY);
            }
            record.setExecIdx(Math.toIntExact(count) + 1);
            // 插入执行任务记录
            record.setId(UUIDUtil.create32UUID());
            record.setStartTime(planTask.getStartTime());   // 任务开始时间
            record.setEndTime(planTask.getEndTime());       // 任务结束时间
            record.setStatus(taskExecResult.getStatus());   // 执行结果
            record.setBatchId(planTask.getBatchId());       // 批次
            record.setErrorMsg(taskExecResult.getMessage());    // 任务错误信息
            record.setExecResultFile(taskExecResult.getFilePath()); // 任务执行记录结果文件地址
            record.setVideoUrl(taskExecResult.getVideoPath());      // 任务执行录屏文件地址
            taskRecordService.insert(record);
        } catch (Exception e) {
            logger.error("新增任务记录失败", e);
        }
    }
}

