package net.northking.cctp.executePlan.job;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import net.northking.cctp.common.enums.FileBusinessTypeEnum;
import net.northking.cctp.common.s3.NKFile;
import net.northking.cctp.common.s3.SimpleStorageService;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import net.northking.cctp.executePlan.api.service.AtuPlanInfoApiService;
import net.northking.cctp.executePlan.api.service.MessageCenterService;
import net.northking.cctp.executePlan.api.third.feilang.service.FeiLangService;
import net.northking.cctp.executePlan.constants.MsgConstant;
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.*;
import net.northking.cctp.executePlan.db.service.*;
import net.northking.cctp.executePlan.dto.planBatch.AtuPlanBatchDetailDto;
import net.northking.cctp.executePlan.dto.planBatch.BatchAppInfoDto;
import net.northking.cctp.executePlan.dto.planBatch.BatchMobilePerformanceDto;
import net.northking.cctp.executePlan.dto.planBatch.MobileTaskPerformanceDto;
import net.northking.cctp.executePlan.dto.planInfo.AtuPlanRunDto;
import net.northking.cctp.executePlan.enums.MobilePlatformEnum;
import net.northking.cctp.executePlan.feign.PublicFeignClient;
import net.northking.cctp.executePlan.utils.MinioPathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import java.io.File;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.stream.Collectors;

/**
 * <p>Title: PlanBatchTaskDataUpdateJob</p>
 * <p>Description: 计划批次任务数据更新</p>
 *
 * @Author chenjianfeng
 * @Version V1.0
 * @Date 2022/12/13
 * <p>Copyright (c) 京北方信息技术股份有限公司 Corporation 2022 . All rights reserved.</p>
 */
@EnableScheduling
@Configuration
public class PlanBatchTaskDataUpdateJob {

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

    @Autowired
    private AtuPlanInfoService planInfoService;

    @Autowired
    private AtuPlanBatchService planBatchService;

    @Autowired
    private AtuPlanTaskService planTaskService;

    @Autowired
    private AtuPlanInfoApiService planInfoApiService;

    @Autowired
    private AmqpAdmin amqpAdmin;

    @Autowired
    private AtuPlanScriptLinkService planScriptLinkService;

    @Autowired
    private AtuPlanAppLinkService planAppLinkService;

    @Autowired
    private AtuPlanDeviceLinkService planDeviceLinkService;

    @Autowired
    private PublicFeignClient publicFeignClient;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private FeiLangService feiLangService;

    @Autowired
    private SimpleStorageService simpleStorageService;

    @Autowired
    private AtuPlanEnvLinkService planEnvLinkService;

    @Autowired
    MessageCenterService messageCenterService;

    public static final int EXECUTOR_CORE_POOL_SIZE = 2 * 2 + 2;
    public static final int EXECUTOR_MAX_POOL_SIZE = 10;
    public static final int EXECUTOR_LINKED_BLOCKING_SIZE = 50;

    private static final ExecutorService executor = ExecutorBuilder.create()
            .setCorePoolSize(EXECUTOR_CORE_POOL_SIZE)
            .setMaxPoolSize(EXECUTOR_MAX_POOL_SIZE)
            .setWorkQueue(new LinkedBlockingDeque<>(EXECUTOR_LINKED_BLOCKING_SIZE))
            .build();

    @Scheduled(fixedRateString = "${atu.plan.batchCountUpdateJob:20000}")
    private void batchSumDataUpdate() {
        logger.debug("同步缓存中计划批次统计数据----start----");
        // 1. 获取缓存中所有的批次统计数据
        String patternKey = RedisConstant.CLUSTER_KEY_PREFIX + "*" + RedisConstant.CLUSTER_KEY_SUFFIX +
                RedisConstant.BATCH_SCRIPT_SUM_KEY + "*";
        Set<String> batchKeySet = redisTemplate.keys(patternKey);
        if (CollUtil.isEmpty(batchKeySet)) {
            logger.debug("不存在批次缓存统计数据----end----");
            return;
        }
        logger.debug("缓存中计划批次统计数据量 => " + batchKeySet.size());
        batchKeySet.forEach(key -> {
            Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
            if (CollUtil.isEmpty(entries)) {
                logger.info("缓存[" + key + "]中无统计数据");
                return;
            }
            String batchId = key.substring(key.lastIndexOf(":") + 1);
            String clusterKeyPrefix = RedisConstant.CLUSTER_KEY_PREFIX + batchId.substring(0, 4) +
                    RedisConstant.CLUSTER_KEY_SUFFIX;
            // 2. 遍历判断该批次是否已完成
            int waitTotal = 0;
            Object waitStatusObj = entries.get(clusterKeyPrefix + PlanConstant.TASK_WAIT_EXECUTE_STATUS);
            if (ObjectUtil.isNotNull(waitStatusObj)) {
                waitTotal = Integer.parseInt(waitStatusObj.toString());
            }
            int runningTotal = 0;
            Object runningStatusObj = entries.get(clusterKeyPrefix + PlanConstant.TASK_START_EXECUTE_STATUS);
            if (ObjectUtil.isNotNull(runningStatusObj)) {
                runningTotal = Integer.parseInt(runningStatusObj.toString());
            }

            if (waitTotal != 0 || runningTotal != 0) {
                // 该批次还未完成
                return;
            }
            // 2.1. 完成则更新数据库中的批次统计数据
            int total = 0;
            Object scriptTotalObj = entries.get(clusterKeyPrefix + PlanConstant.SCRIPT_TOTAL);
            if (ObjectUtil.isNotNull(scriptTotalObj)) {
                total = Integer.parseInt(scriptTotalObj.toString());
            }
            int successTotal = 0;
            Object successStatusObj = entries.get(clusterKeyPrefix + PlanConstant.TASK_EXECUTE_SUCCESS_STATUS);
            if (ObjectUtil.isNotNull(successStatusObj)) {
                successTotal = Integer.parseInt(successStatusObj.toString());
            }
            int failTotal = 0;
            Object failStatusObj = entries.get(clusterKeyPrefix + PlanConstant.TASK_EXECUTE_FAIL_STATUS);
            if (ObjectUtil.isNotNull(failStatusObj)) {
                failTotal = Integer.parseInt(failStatusObj.toString());
                if (failTotal < 0) {
                    failTotal = 0;
                }
            }
            int assertFailTotal = 0;
            Object assertFailStatusObj = entries.get(clusterKeyPrefix + PlanConstant.TASK_ASSERT_FAIL_STATUS);
            if (ObjectUtil.isNotNull(assertFailStatusObj)) {
                assertFailTotal = Integer.parseInt(assertFailStatusObj.toString());
            }
            int timeoutTotal = 0;
            Object timeoutStatusObj = entries.get(clusterKeyPrefix + PlanConstant.TASK_TIMEOUT_STATUS);
            if (ObjectUtil.isNotNull(timeoutStatusObj)) {
                timeoutTotal = Integer.parseInt(timeoutStatusObj.toString());
            }
            int cancelTotal = 0;
            Object cancelStatusObj = entries.get(clusterKeyPrefix + PlanConstant.TASK_CANCEL_STATUS);
            if (ObjectUtil.isNotNull(cancelStatusObj)) {
                cancelTotal = Integer.parseInt(cancelStatusObj.toString());
            }

            // 更新批次表
            AtuPlanBatch planBatch = new AtuPlanBatch();
            planBatch.setId(batchId);
            planBatch.setSuccessNum(successTotal);
            planBatch.setExecFailNum(failTotal);
            planBatch.setAssertFailNum(assertFailTotal);
            planBatch.setTimeoutNum(timeoutTotal);
            planBatch.setCancelNum(cancelTotal);

            planBatch.setSuccessRate(MsgConstant.PLAN_BATCH_SUCCESS_RATE);
            if (successTotal > 0 && total > 0) {
                planBatch.setSuccessRate(NumberUtil.decimalFormat(MsgConstant.PATTERN_HASH_DOT_HASH_HASH, NumberUtil.div(successTotal, total)));
            }
            // 查询批次最先开始执行的任务的开始时间
            if (planBatch.getStartTime() == null) {
                Long firstTaskStartTime = planTaskService.queryBatchFirstTaskStartTime(batchId);
                if (ObjectUtil.isNotNull(firstTaskStartTime)) {
                    planBatch.setStartTime(new Date(firstTaskStartTime));
                }
            }
            // 任务状态判断
            String planStatus = "";
            if (cancelTotal > 0) {
                planStatus = PlanConstant.PLAN_CANCEL_STATUS;
                // 修改为取消状态
                planBatch.setStatus(PlanConstant.BATCH_CANCEL_STATUS);
            } else {
                planStatus = PlanConstant.PLAN_FINISH_STATUS;
                // 修改为已完成状态
                planBatch.setStatus(PlanConstant.BATCH_FINISH_STATUS);
            }

            // 查询批次最后执行完成的任务的结束时间
            Long lastTaskEndTime = planTaskService.queryBatchLastTaskEndTime(batchId);
            if (ObjectUtil.isNotNull(lastTaskEndTime)) {
                planBatch.setEndTime(new Date(lastTaskEndTime));
            }
            logger.debug("更新批次信息");
            planBatchService.updateByPrimaryKey(planBatch);

            // 删除任务队列
            amqpAdmin.deleteQueue(RabbitConstant.TASK_EXEC_QUEUE_PC_KEY + batchId);
            // 移动端删除各个平台对于的队列
            MobilePlatformEnum[] platformEnums = MobilePlatformEnum.values();
            for (MobilePlatformEnum platformEnum : platformEnums) {
                amqpAdmin.deleteQueue(RabbitConstant.TASK_EXEC_QUEUE_MOB_KEY + platformEnum.getName() + "." + batchId);
            }
            amqpAdmin.deleteQueue(RabbitConstant.TASK_EXEC_QUEUE_API_KEY + batchId);

            //获取更新后的批次信息
            AtuPlanBatchDetailDto atuPlanBatchDetailDto = planBatchService.queryBatchDetailById(planBatch.getId());
            // 收尾处理
            AtuPlanInfo batchPlanInfo = planInfoService.findByBatchId(batchId);
            if (batchPlanInfo != null) {
                handleEnd(batchPlanInfo, atuPlanBatchDetailDto);
            }
            // 判断是否计划最后一批次
            AtuPlanInfo planInfo = planInfoService.queryByLastBatchId(batchId);
            if (ObjectUtil.isNotNull(planInfo)) {
                // 是则更新计划表
                planInfo.setStatus(planStatus);
                planInfo.setWaitingNum(waitTotal);
                planInfo.setRunningNum(runningTotal);
                planInfo.setSuccessNum(successTotal);
                planInfo.setExecFailNum(failTotal);
                planInfo.setAssertFailNum(assertFailTotal);
                planInfo.setTimeoutNum(timeoutTotal);
                planInfo.setCancelNum(cancelTotal);
                //不用代码生成的方法
                planInfoService.updatePlanResultById(planInfo);
            }

            executor.execute(() -> {
                // 保存设备性能数据
                saveDevicePerData(atuPlanBatchDetailDto);
            });

            // 2.2. 删除缓存中已完成批次的记录
            logger.debug("删除批次统计缓存 => " + key);
            redisTemplate.delete(key);
        });
        logger.debug("同步缓存中计划批次统计数据----end----");
    }

    private void saveDevicePerData(AtuPlanBatchDetailDto atuPlanBatchDetailDto) {
        String batchId = atuPlanBatchDetailDto.getId();
        String tenantId = atuPlanBatchDetailDto.getTenantId();
        if (StrUtil.isBlank(tenantId)) {
            logger.error("获取批次计划中的租户id异常");
            return;
        }
        logger.debug("统计并保存批次设备性能数据");
        // 查询该批次下所有的移动端任务性能文件地址
        List<String> perDataPathList = planTaskService.queryMobilePerDataPathByBatchId(batchId);
        if (CollUtil.isEmpty(perDataPathList)) {
            logger.debug("该批次无性能数据");
            return;
        }
        logger.debug("perDataPathList => {}", JSONUtil.toJsonStr(perDataPathList));
        Map<String, BatchMobilePerformanceDto> deviceAppPerMap = new HashMap<>();
        for (String perDataPath : perDataPathList) {
            MobileTaskPerformanceDto taskPerInfo = null;
            try {
                taskPerInfo = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
                        tenantId, perDataPath, MobileTaskPerformanceDto.class);
            } catch (Exception e) {
                logger.error("获取任务性能数据异常：{}", perDataPath);
            }
            logger.debug("taskPerInfo => {}", JSONUtil.toJsonStr(taskPerInfo));
            if (taskPerInfo == null) {
                logger.debug("任务性能数据为空：{}", perDataPath);
                continue;
            }
            String deviceAppKey = taskPerInfo.getDeviceId() + "-" + taskPerInfo.getAppId();
            BatchMobilePerformanceDto deviceAppPer = new BatchMobilePerformanceDto();
            if (deviceAppPerMap.containsKey(deviceAppKey)) {
                deviceAppPer = deviceAppPerMap.get(deviceAppKey);
            }
            // 添加安装时间
            deviceAppPer.setAppInstallTimeList(defaultListAddData(deviceAppPer.getAppInstallTimeList(), taskPerInfo.getAppInstallTime()));
            // 添加启动时间
            deviceAppPer.setAppActiveTimeList(defaultListAddData(deviceAppPer.getAppActiveTimeList(), taskPerInfo.getAppActiveTime()));
            deviceAppPer.setAppCpuList(defaultListAddList(deviceAppPer.getAppCpuList(), taskPerInfo.getAppCpuList()));
            deviceAppPer.setAppMemoList(defaultListAddList(deviceAppPer.getAppMemoList(), taskPerInfo.getAppMemoList()));
            deviceAppPer.setAppFlowList(defaultListAddList(deviceAppPer.getAppFlowList(), taskPerInfo.getAppFlowList()));
            deviceAppPer.setDeviceCpuList(defaultListAddList(deviceAppPer.getDeviceCpuList(), taskPerInfo.getDeviceCpuList()));
            deviceAppPer.setDeviceMemoList(defaultListAddList(deviceAppPer.getDeviceMemoList(), taskPerInfo.getDeviceMemoList()));
            deviceAppPer.setDeviceFlowList(defaultListAddList(deviceAppPer.getDeviceFlowList(), taskPerInfo.getDeviceFlowList()));
            deviceAppPer.setDeviceTemperatureList(defaultListAddList(deviceAppPer.getDeviceTemperatureList(), taskPerInfo.getDeviceTemperatureList()));
            deviceAppPerMap.put(deviceAppKey, deviceAppPer);
        }
        logger.debug("deviceAppPerMap => {}", JSONUtil.toJsonStr(deviceAppPerMap));
        if (CollUtil.isEmpty(deviceAppPerMap)) {
            logger.debug("任务性能数据整合后为空");
            return;
        }

        List<BatchAppInfoDto> appInfoDtoList = new ArrayList<>();
        deviceAppPerMap.forEach((deviceAppKey, deviceAppPer) -> {
            BatchAppInfoDto appInfoDto = new BatchAppInfoDto();
            appInfoDto.setDeviceId(deviceAppKey.split("-")[0]);
            appInfoDto.setAppId(deviceAppKey.split("-")[1]);

            // APP安装耗时
            appInfoDto.setInstallTime(getListAvgValueDiv1000(deviceAppPer.getAppInstallTimeList(), MsgConstant.LETTER_S));
            appInfoDto.setMaxInstallTime(getListMaxValueDiv1000(deviceAppPer.getAppInstallTimeList(), MsgConstant.LETTER_S));

            // app启动耗时
            appInfoDto.setStartElapsedTime(getListAvgValueDiv1000(deviceAppPer.getAppActiveTimeList(), MsgConstant.LETTER_S));
            appInfoDto.setMaxStartElapsedTime(getListMaxValueDiv1000(deviceAppPer.getAppActiveTimeList(), MsgConstant.LETTER_S));

            // app cpu使用率
            appInfoDto.setAppCpu(getListAvgValue(deviceAppPer.getAppCpuList(), MsgConstant.PERCENTAGE));
            appInfoDto.setMaxAppCpu(getListMaxValue(deviceAppPer.getAppCpuList(), MsgConstant.PERCENTAGE));

            // app内存使用
            appInfoDto.setAppMemory(getListAvgValue(deviceAppPer.getAppMemoList(), MsgConstant.LETTER_M));
            appInfoDto.setMaxAppMemory(getListMaxValue(deviceAppPer.getAppMemoList(), MsgConstant.LETTER_M));

            // app流量使用
            appInfoDto.setAppFlow(getListAvgValue(deviceAppPer.getAppFlowList(), MsgConstant.LETTER_M));
            appInfoDto.setMaxAppFlow(getListMaxValue(deviceAppPer.getAppFlowList(), MsgConstant.LETTER_M));

            // 设备cpu使用率
            appInfoDto.setDeviceCpu(getListAvgValue(deviceAppPer.getDeviceCpuList(), MsgConstant.PERCENTAGE));
            appInfoDto.setMaxDeviceCpu(getListMaxValue(deviceAppPer.getDeviceCpuList(), MsgConstant.PERCENTAGE));

            // 设备内存使用
            appInfoDto.setDeviceMemory(getListAvgValue(deviceAppPer.getDeviceMemoList(), MsgConstant.LETTER_M));
            appInfoDto.setMaxDeviceMemory(getListMaxValue(deviceAppPer.getDeviceMemoList(), MsgConstant.LETTER_M));

            // 设备流量使用
            appInfoDto.setDeviceFlow(getListAvgValue(deviceAppPer.getDeviceFlowList(), MsgConstant.LETTER_M));
            appInfoDto.setMaxDeviceFlow(getListMaxValue(deviceAppPer.getDeviceFlowList(), MsgConstant.LETTER_M));

            // 设备电池温度
            appInfoDto.setCellTemperature(getListAvgValue(deviceAppPer.getDeviceTemperatureList(), MsgConstant.TEMPERATURE_DEGREE));
            appInfoDto.setMaxCellTemperature(getListMaxValue(deviceAppPer.getDeviceTemperatureList(), MsgConstant.TEMPERATURE_DEGREE));
            appInfoDtoList.add(appInfoDto);
        });

        logger.debug("设备性能数据为：{}", appInfoDtoList.size());
        // 保存整合数据
        File file = MinioPathUtils.objectToJsonFile(appInfoDtoList);
        try {
            NKFile nkFile = simpleStorageService.upload(tenantId, file, true, batchId, FileBusinessTypeEnum.BATCH_TASK_PERFORMANCE_SUMMARY);
            String path = nkFile.getId();
            logger.debug("整合数据保存地址 => {}", path);
            AtuPlanBatch byPrimaryKey = this.planBatchService.findByPrimaryKey(batchId);
            String oldPerDataPath = byPrimaryKey.getPerDataPath();
            // 保存批次性能文件地址
            AtuPlanBatch planBatch = new AtuPlanBatch();
            planBatch.setId(batchId);
            planBatch.setPerDataPath(path);
            this.planBatchService.updateByPrimaryKey(planBatch);
            // 删除旧的性能文件
            if (StrUtil.isNotBlank(oldPerDataPath)){
                String[] pathUrl = MinioPathUtils.idToPath(oldPerDataPath);
                try {
                    simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
                } catch (Exception e) {
                    logger.error("文件删除删除失败", e);
                }
            }
        } catch (Exception e) {
            logger.error("文件上传异常", e);
        }
    }

    private String getListMaxValue(List<Double> list, String union) {
        String maxValue = "0.00" + union;
        if (CollUtil.isNotEmpty(list)) {
            double max = list.stream().mapToDouble(Double::doubleValue).max().orElse(0);
            maxValue = max + union;
        }
        return maxValue;
    }

    private String getListAvgValue(List<Double> list, String union) {
        String avgValue = MsgConstant.ZERO_DOT + union;
        if (CollUtil.isNotEmpty(list)) {
            double avg = list.stream().mapToDouble(Double::doubleValue).average().orElse(0);
            avgValue = NumberUtil.roundStr(avg, 2) + union;
        }
        return avgValue;
    }

    private String getListMaxValueDiv1000(List<Long> list, String union) {
        String maxValue = MsgConstant.ZERO_DOT + union;
        if (CollUtil.isNotEmpty(list)) {
            long max = list.stream().mapToLong(Long::longValue).max().orElse(0);
            double maxDiv = NumberUtil.div(max, 1000);
            maxValue = NumberUtil.round(maxDiv, 2) + union;
        }
        return maxValue;
    }

    private String getListAvgValueDiv1000(List<Long> list, String union) {
        String avgValue = MsgConstant.ZERO_DOT + union;
        if (CollUtil.isNotEmpty(list)) {
            double avg = list.stream().mapToLong(Long::longValue).average().orElse(0);
            double avgDiv = NumberUtil.div(avg, 1000);
            avgValue = NumberUtil.round(avgDiv, 2) + union;
        }
        return avgValue;
    }

    private static List<Long> defaultListAddData(List<Long> list, Long data) {
        if (CollUtil.isEmpty(list)) {
            list = CollUtil.newArrayList();
        }
        if (data == null) {
            return list;
        }
        list.add(data);
        return list;
    }

    private static List<Double> defaultListAddList(List<Double> list, List<Double> data) {
        if (CollUtil.isEmpty(list)) {
            list = CollUtil.newArrayList();
        }
        if (CollUtil.isEmpty(data)) {
            return list;
        }
        list.addAll(data);
        return list;
    }

    /**
     * 收尾处理
     *
     * @param planInfo  批次计划信息
     * @param planBatch 批次信息
     */
    private void handleEnd(AtuPlanInfo planInfo, AtuPlanBatchDetailDto planBatch) {
        // 计划是否被其他计划设为前置执行计划
        List<String> planIdList = planInfoService.queryFollowUpPlanIdList(planInfo.getId());
        if (CollUtil.isNotEmpty(planIdList)) {
            for (String id : planIdList) {
                AtuPlanRunDto atuPlanRunDto = new AtuPlanRunDto();
                atuPlanRunDto.setPlanId(id);
                atuPlanRunDto.setExecutedBy(planInfo.getPrincipalId());
                atuPlanRunDto.setTriggerType(PlanConstant.TRIGGER_TYPE_AUTO);
                planInfoApiService.run(atuPlanRunDto);
            }
        }
        // 计划是否需要发送邮件，失败用例是否生成失败计划
        NKSecurityContext.setTenantId(planInfo.getTenantId());
        NKSecurityContext.setUserId(planInfo.getCreatedBy());
        // 发送到飞狼

        executor.execute(() -> {
            //推送消息中心弹窗
            try {
                messageCenterService.sendMessage(planInfo, planBatch);
            } catch (Exception e) {
                logger.error("推送消息中心失败" + e);
            }
            //判断是否发送到飞狼 此处
            if (planInfo.getSendMail()) {
                // 判断飞狼user是否存在
                if (StrUtil.isNotBlank(planInfo.getMailAddress())) {
                    try {
                        feiLangService.sendMessage(planInfo, planBatch);
                    } catch (Exception e) {
                        logger.error("飞狼推送失败", e);
                    }
                }
            }
        });

        // 判断执行失败是否生成新的计划
        if (planInfo.getFailCreatePlan()) {
            // 判断是否有执行失败的任务
            if (planBatch.getExecFailNum() > 0) {
                // 生成新的计划信息
                createFailCasePlan(planInfo, planBatch);
            }
        }
    }

    /**
     * 生成失败用例计划
     *
     * @param planInfo  原计划信息
     * @param planBatch 批次信息
     */
    private void createFailCasePlan(AtuPlanInfo planInfo, AtuPlanBatchDetailDto planBatch) {
        String batchId = planBatch.getId();
        // 根据失败任务查询失败用例，并生成新的用例集
        AtuPlanTask queryParams = new AtuPlanTask();
        queryParams.setBatchId(batchId);
        List<AtuPlanTask> planTaskList = planTaskService.query(queryParams);
        if (CollUtil.isEmpty(planTaskList)) {
            logger.debug("批次[{}]无任务生成", batchId);
            return;
        }

        // 将任务根据脚本进行分组
        Map<String, List<AtuPlanTask>> scriptTaskMap = planTaskList.stream()
                .collect(Collectors.groupingBy(AtuPlanTask::getScriptId));
        Set<Map.Entry<String, List<AtuPlanTask>>> entries = scriptTaskMap.entrySet();
        String newPlanId = IdUtil.simpleUUID();
        List<AtuPlanScriptLink> scriptLinkList = new ArrayList<>();
        for (Map.Entry<String, List<AtuPlanTask>> entry : entries) {
            String scriptId = entry.getKey();
            List<AtuPlanTask> scriptTaskList = entry.getValue();
            // 将任务分成成功和失败
            List<AtuPlanTask> successTaskList = scriptTaskList.stream()
                    .filter(task -> task.getStatus().equals(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS))
                    .collect(Collectors.toList());
            // 失败任务
            scriptTaskList.removeAll(successTaskList);
            if (CollUtil.isEmpty(scriptTaskList)) {
                continue;
            }
            // 将脚本绑定至新计划
            AtuPlanTask planTask = scriptTaskList.get(0);
            AtuPlanScriptLink scriptLink = new AtuPlanScriptLink();
            scriptLink.setPlanId(newPlanId);
            scriptLink.setScriptId(scriptId);
            scriptLink.setScriptType(planTask.getCaseType());
            scriptLinkList.add(scriptLink);
        }

        if (CollUtil.isEmpty(scriptLinkList)) {
            logger.debug("任务全部成功");
            return;
        }

        // 复制计划信息（修改计划名称，去除触发方式信息）
        AtuPlanInfo newPlan = new AtuPlanInfo();
        BeanUtil.copyProperties(planInfo, newPlan);
        newPlan.setId(newPlanId);
        String planName = planInfo.getPlanName() + "-" + planBatch.getBatch() + "-失败用例";
        // 校验名称是否存在
        int num = 0;
        while (true) {
            AtuPlanInfo countPlan = new AtuPlanInfo();
            String name = num == 0 ? planName : planName + "(" + num + ")";
            countPlan.setPlanName(name);
            long count = planInfoService.count(countPlan);
            if (count > 0) {
                num++;
            } else {
                planName = name;
                break;
            }
        }
        newPlan.setPlanName(planName);
        newPlan.setCronExpr("");
        newPlan.setUpFrontPlanId("");
        newPlan.setWebhookUrl("");
        newPlan.setWebhookToken("");
        //补充
        newPlan.setHasCorn(false);
        newPlan.setHasUpFront(false);
        newPlan.setHasWebhook(false);
        newPlan.setLastBatchId("");
        newPlan.setWaitingNum(0);
        newPlan.setRunningNum(0);
        newPlan.setSuccessNum(0);
        newPlan.setExecFailNum(0);
        newPlan.setAssertFailNum(0);
        newPlan.setTimeoutNum(0);
        newPlan.setCancelNum(0);
        //重置状态为新建
        newPlan.setStatus(PlanConstant.PLAN_NEW_STATUS);
        newPlan.setLastExecTime(null);
        newPlan.setNextExecTime(null);
        newPlan.setPrincipalId(planBatch.getExecutedBy());
        planInfoService.insert(newPlan);

        // 绑定应用
        AtuPlanAppLink appLinkQuery = new AtuPlanAppLink();
        appLinkQuery.setPlanId(planInfo.getId());
        List<AtuPlanAppLink> appLinkList = planAppLinkService.query(appLinkQuery);
        if (CollUtil.isNotEmpty(appLinkList)) {
            for (AtuPlanAppLink planAppLink : appLinkList) {
                planAppLink.setId(IdUtil.simpleUUID());
                planAppLink.setPlanId(newPlan.getId());
            }
            planAppLinkService.insertByBatch(appLinkList);
        }
        // 绑定设备
        AtuPlanDeviceLink deviceLinkQuery = new AtuPlanDeviceLink();
        deviceLinkQuery.setPlanId(planInfo.getId());
        List<AtuPlanDeviceLink> deviceLinkList = planDeviceLinkService.query(deviceLinkQuery);
        if (CollUtil.isNotEmpty(deviceLinkList)) {
            for (AtuPlanDeviceLink planDeviceLink : deviceLinkList) {
                planDeviceLink.setId(IdUtil.simpleUUID());
                planDeviceLink.setPlanId(newPlan.getId());
            }
            planDeviceLinkService.insertByBatch(deviceLinkList);
        }

        // 绑定环境
        AtuPlanEnvLink envLinkQuery = new AtuPlanEnvLink();
        envLinkQuery.setPlanId(planInfo.getId());
        List<AtuPlanEnvLink> envLinkList = planEnvLinkService.query(envLinkQuery);
        List<AtuPlanEnvLink> newEnvLinkList = new ArrayList<>();
        if (CollUtil.isNotEmpty(envLinkList)) {
            for (AtuPlanEnvLink envLink : envLinkList) {
                AtuPlanEnvLink newEnvLink = new AtuPlanEnvLink();
                newEnvLink.setPlanId(newPlanId);
                newEnvLink.setEnvId(envLink.getEnvId());
                newEnvLink.setSort(envLink.getSort());
                newEnvLinkList.add(newEnvLink);
            }
            planEnvLinkService.insertByBatch(newEnvLinkList);
        }

        // 绑定脚本
        planScriptLinkService.insertByBatch(scriptLinkList);
    }
}
