package com.css.simulation.resource.scheduler.domain.service;

import api.common.pojo.constants.DictConstants;
import api.common.pojo.param.scene.SceneEvaluationComputeParam;
import api.common.pojo.param.scene.SceneEvaluationComputeSubParam;
import api.common.util.*;
import com.alibaba.druid.util.StringUtils;
import com.css.simulation.resource.scheduler.app.repository.TaskIndexRepository;
import com.css.simulation.resource.scheduler.infra.configuration.custom.CustomConfiguration;
import com.css.simulation.resource.scheduler.infra.db.entity.ScoringRuleEntity;
import com.css.simulation.resource.scheduler.infra.db.mysql.mapper.*;
import com.css.simulation.resource.scheduler.infra.db.redis.RedisUtil;
import com.css.simulation.resource.scheduler.infra.entity.*;
import com.css.simulation.resource.scheduler.infra.fs.minio.MinioUtil;
import com.css.simulation.resource.scheduler.infra.mq.kafka.KafkaUtil;
import com.css.simulation.resource.scheduler.infra.runnable.SceneEvaluationComputeRunnable;
import com.css.simulation.resource.scheduler.infra.threadpool.ThreadPool;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.minio.MinioClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.kafka.clients.admin.Admin;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@Slf4j
public class TaskDomainService {

    @Value("${minio.bucket-name}")
    private String bucketName;
    @Value("${scheduler.linux-path.score-py}")
    private String pyPath;
    @Value("${scheduler.linux-path.temp}")
    private String linuxTempPath;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private SimulationManualProjectTaskMapper simulationManualProjectTaskMapper;
    @Resource
    private MinioClient minioClient;
    @Resource
    private SimulationManualProjectMapper simulationManualProjectMapper;
    @Resource
    private SimulationAutomaticSubprojectMapper simulationAutomaticSubprojectMapper;
    @Resource
    private ScoringRulesMapper scoringRulesMapper;
    @Resource
    private TaskIndexRepository taskIndexRepository;
    @Resource
    private IndexMapper indexMapper;
    @Resource
    private ProjectDomainService projectDomainService;
    @Resource
    private SqlSessionFactory sqlSessionFactory;
    @Resource(name = "myKafkaAdmin")
    private Admin admin;
    @Resource
    private CustomConfiguration customConfiguration;
    @Resource
    private SceneMapper sceneMapper;
    @Resource
    private IndexTemplateMapper indexTemplateMapper;

    public void batchInsertTask(List<TaskEntity> taskEntityList) {
        try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
            SimulationManualProjectTaskMapper simulationManualProjectTaskMapper1 = sqlSession.getMapper(SimulationManualProjectTaskMapper.class);
            for (TaskEntity taskEntity : taskEntityList) {
                simulationManualProjectTaskMapper1.insert(taskEntity);
            }
            sqlSession.commit();
        }
    }

    /**
     * @param projectUserId 项目创建用户的 id
     */
    @SneakyThrows
    public void score(String projectRunningKey, String projectUserId, String projectId, String projectType) {
        stringRedisTemplate.delete(projectRunningKey);
        // -------------------------------- 打分 --------------------------------
        ProjectEntity projectEntity = null;
        if (DictConstants.PROJECT_TYPE_MANUAL.equals(projectType)) {
            projectEntity = simulationManualProjectMapper.selectById(projectId);
        } else if (DictConstants.PROJECT_TYPE_AUTO_SUB.equals(projectType)) {
            projectEntity = simulationAutomaticSubprojectMapper.selectById(projectId);
        }
        if (projectEntity == null) {
            log.error("不存在项目 {}", projectId);
            return;
        }
        String packageId = projectEntity.getScenePackageId();  // 场景测试包 id，指标的rootId
        TimeUnit.SECONDS.sleep(3); // 先等一下数据库更新
        List<TaskEntity> taskList = simulationManualProjectTaskMapper.selectTaskListByProjectId(projectId);  // 所有任务信息
        if (CollectionUtil.isEmpty(taskList)) {
            log.error("项目 {} 下没有查询到任务！", projectId);
            return;
        }
        indexMapper.deleteFirstByProjectId(projectId);
        indexMapper.deleteLastByProjectId(projectId);
        //1 查询场景包对应指标
        String allIndexKey = "project:" + projectId + ":package:" + packageId + ":all";
        String leafIndexKey = "project:" + projectId + ":package:" + packageId + ":leaf";
        String allIndexTemplateListJson = stringRedisTemplate.opsForValue().get(allIndexKey);
        String leafIndexTemplateListJson = stringRedisTemplate.opsForValue().get(leafIndexKey);
        List<IndexTemplateEntity> allIndexTemplateList = JsonUtil.jsonToList(allIndexTemplateListJson, IndexTemplateEntity.class);
        List<IndexTemplateEntity> leafIndexTemplateList = JsonUtil.jsonToList(leafIndexTemplateListJson, IndexTemplateEntity.class);
        log.debug("共有 " + leafIndexTemplateList.size() + "个叶子节点：" + leafIndexTemplateListJson);
        int maxLevel = 1; // 用于计算指标得分
        List<SceneEntity> sceneEntityList = getSceneList(projectId, packageId);
        Map<String, SceneEntity> sceneEntityMap = sceneEntityList.stream().collect(Collectors.toMap(SceneEntity::getId, Function.identity()));
        List<LeafIndexEntity> leafIndexList = new ArrayList<>();
        SceneEvaluationComputeParam sceneComplexityEvaluationComputeParam = new SceneEvaluationComputeParam();
        sceneComplexityEvaluationComputeParam.setSceneEvaluationComputeSubParam(new ArrayList<>());
        SceneEvaluationComputeParam sceneRiskEvaluationComputeParam = new SceneEvaluationComputeParam();
        sceneRiskEvaluationComputeParam.setSceneEvaluationComputeSubParam(new ArrayList<>());
        for (int i = 0; i < leafIndexTemplateList.size(); i++) {
            String scoreExplain = null; // 每个叶子指标下的任务的得分说明一样和叶子指标一致
            IndexTemplateEntity leafIndexTemplate = leafIndexTemplateList.get(i);
            String indexId = leafIndexTemplate.getIndexId(); // 叶子指标id
            String parentId = leafIndexTemplate.getParentId(); // 父 id
            String rootId = leafIndexTemplate.getRootId(); // 包 id
            String weight = leafIndexTemplate.getWeight(); // 权重
            Integer packageLevel = leafIndexTemplate.getPackageLevel(); // 几级指标
            final String ruleNameArray = leafIndexTemplate.getRuleNameArray();
            final String rulesId = ruleNameArray.split(",")[1];
            String ruleName = scoringRulesMapper.selectRuleNameByRulesId(ScoringRuleEntity.builder().rulesId(rulesId).build());
            String ruleDetails = leafIndexTemplate.getRuleDetails();    // 打分脚本内容
            if (packageLevel > maxLevel) {
                maxLevel = packageLevel;
            }
            log.info("开始执行对第 " + (i + 1) + " 个叶子节点 " + indexId + " 进行打分！");
            // 根据叶子指标id查询评分规则创建用户id
            String createUserIdOfRule = scoringRulesMapper.selectCreateUserIdByIndexId(indexId);
            //1 判断有没有用户目录，没有则复制
            String scoreDirectoryOfUser = linuxTempPath + "score/" + createUserIdOfRule + "/";
            if (!new File(scoreDirectoryOfUser + "main.py").exists()) {
                // 复制 main.py
                FileUtil.createDirectory(scoreDirectoryOfUser);
                FileUtil.cpR(pyPath, scoreDirectoryOfUser);
            }
            //2 将打分规则保存到script目录

            String ruleFilePath = scoreDirectoryOfUser + "scripts/" + ruleName.split("_")[0] + "/" + ruleName + ".py";
            FileUtil.writeInputStreamToLocalFile(IoUtil.stringToInputStream(ruleDetails), ruleFilePath);
            log.info("将叶子节点 " + indexId + " 对应的打分规则保存到临时目录：" + ruleFilePath);
            List<TaskEntity> taskListOfLeafIndex = taskList.stream().filter(task -> indexId.equals(task.getLastTargetId())).collect(Collectors.toList());
            log.info("叶子节点 " + indexId + " 包括 " + taskListOfLeafIndex.size() + " 个任务：" + taskListOfLeafIndex);
            // 计算叶子指标的得分
            // 使用 stream 流会出现无法进入循环的情况
            for (TaskEntity taskOfLeaf : taskListOfLeafIndex) {
                String task2Id = taskOfLeaf.getId();
                // 计算复杂度和危险度
                TaskEntity taskEntity = simulationManualProjectTaskMapper.selectById(task2Id);
                log.info("查询任务实体： " + taskEntity);
                String sceneId = taskEntity.getSceneId();
                if (!StringUtils.isEmpty(projectEntity.getComplexityEvaluationRuleId())) {
                    SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam = new SceneEvaluationComputeSubParam();
                    sceneEvaluationComputeSubParam.setSceneId(sceneId);
                    sceneEvaluationComputeSubParam.setSceneXOSCPath(sceneEntityMap.get(sceneId).getScenarioOsc());
                    sceneEvaluationComputeSubParam.setSceneXODRPath(sceneEntityMap.get(sceneId).getScenarioOdr());
                    sceneEvaluationComputeSubParam.setSceneType(sceneEntityMap.get(sceneId).getType());
                    sceneComplexityEvaluationComputeParam.getSceneEvaluationComputeSubParam().add(sceneEvaluationComputeSubParam);
                }
                String runState = taskOfLeaf.getRunState();
                log.debug("任务 " + task2Id + " 的运行状态为：" + runState);
                if (DictConstants.TASK_ANALYSIS.equals(runState)) {
                    simulationManualProjectTaskMapper.updateSuccessStateWithStopTime(task2Id, DictConstants.TASK_ANALYSING, TimeUtil.getNowForMysql());
                    // 计算每个任务的得分
                    final String runResultFilePath = taskOfLeaf.getRunResultFilePath();

                    // 计算复杂度和危险度
                    if (!StringUtils.isEmpty(projectEntity.getRiskEvaluationRuleId())) {
                        SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam = new SceneEvaluationComputeSubParam();
                        sceneEvaluationComputeSubParam.setSceneId(sceneId);
                        sceneEvaluationComputeSubParam.setEvaluationPath(runResultFilePath);
                        sceneEvaluationComputeSubParam.setSceneType(sceneEntityMap.get(sceneId).getType());
                        sceneRiskEvaluationComputeParam.getSceneEvaluationComputeSubParam().add(sceneEvaluationComputeSubParam);
                    }
                    final ArrayList<String> csvResultFilePaths = CollectionUtil.createArrayList(
                            runResultFilePath + "/Ego.csv",
                            runResultFilePath + "/evaluation.csv",
                            runResultFilePath + "/output_objectState_sensor.csv",
                            runResultFilePath + "/output_objectState.csv",
                            runResultFilePath + "/output_roadMark.csv",
                            runResultFilePath + "/output_roadPos.csv",
                            runResultFilePath + "/output_trafficSignal.csv",
                            runResultFilePath + "/output_trafficLight.csv",
                            runResultFilePath + "/output_trafficLight.csv",
                            runResultFilePath + "/output_driverCtrl.csv",
                            runResultFilePath + "/trajectory.txt"

                    );
                    for (String csvResultFileMinioPath : csvResultFilePaths) {
                        if (!MinioUtil.isObjectExist(minioClient, bucketName, csvResultFileMinioPath)) {
                            log.info("没有生成文件" + csvResultFileMinioPath + "，该文件不参与评分。");
                            continue;
                        }
                        String csvResultFileLinuxPath = linuxTempPath + csvResultFileMinioPath;
                        log.info("下载MINIO上的结果文件：" + csvResultFileMinioPath + " 到LINUX：" + csvResultFileLinuxPath);
                        MinioUtil.downloadToFile(minioClient, bucketName, csvResultFileMinioPath, csvResultFileLinuxPath);  // 也可改成下载到指定ip的服务器上，需要保证和打分脚本在一台机器上。
                    }

                    String scoreCommand = "python3 " + scoreDirectoryOfUser + "main.py " + (linuxTempPath + runResultFilePath + "/Ego.csv") + " " + (linuxTempPath + runResultFilePath + "/evaluation.csv") + " " + taskOfLeaf.getSceneType() + " " + ruleName; // 指定打分脚本
                    String scoreResult;
                    ScoreEntity score = null;
                    log.info("开始执行打分命令：" + scoreCommand);
                    Runtime r = Runtime.getRuntime();
                    Process p = r.exec(scoreCommand, null, new File(scoreDirectoryOfUser));
                    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
                    StringBuilder sb = new StringBuilder();
                    String inline;
                    while (null != (inline = br.readLine())) {
                        sb.append(inline).append("\n");
                    }
                    scoreResult = sb.toString();
                    log.info("项目" + projectId + " 的任务 " + task2Id + " 打分结束，结果为：" + scoreResult);
                    String replace = StringUtil.replace(scoreResult, "'", "\"");
                    try {
                        score = JsonUtil.jsonToBean(replace, ScoreEntity.class);
                    } catch (Exception e) { // 打分失败
                        log.debug("项目" + projectId + " 的任务 " + task2Id + " 打分失败：", e);
                    }
                    if (score != null) {
                        taskOfLeaf.setReturnSceneId(score.getUnit_scene_ID());
                        taskOfLeaf.setTargetEvaluate(score.getEvaluate_item());
                        taskOfLeaf.setScoreExplain(score.getScore_description());
                        taskOfLeaf.setTimeActual(score.getTime_actual());
                        taskOfLeaf.setTimeCorrection(score.getTime_correction());
                        taskOfLeaf.setModifyUserId(projectUserId);
                        taskOfLeaf.setModifyTime(TimeUtil.getNowForMysql());
                        scoreExplain = score.getScore_description();
                        taskOfLeaf.setRunState(DictConstants.TASK_COMPLETED);
                        double taskScore = score.getUnit_scene_score();
                        if (taskScore == -1.0) {
                            taskOfLeaf.setScore(0.0);
                            taskOfLeaf.setScored(false);
                        } else {
                            taskOfLeaf.setScore(NumberUtil.cut(taskScore, 2));
                            taskOfLeaf.setScored(true);
                        }
                        simulationManualProjectTaskMapper.updateSuccessState(taskOfLeaf, DictConstants.TASK_COMPLETED);
                    } else {
                        //1 修改任务状态为 aborted
                        taskOfLeaf.setRunState(DictConstants.TASK_ABORTED);
                        taskOfLeaf.setScore(0.0);
                        taskOfLeaf.setScored(false);
                        simulationManualProjectTaskMapper.updateFailStateWithStopTime(task2Id, DictConstants.TASK_ABORTED, TimeUtil.getNowForMysql(), DictConstants.TASK_ERROR_REASON_5);
                    }
                }
            }

            // 全部参与计算
            // 计算不合格的任务数（不到100分就是不合格，执行失败的不算）
            // 计算叶子指标下任务得分总和
            int errorSceneNumber = 0;   // 仿真失败的任务
            int notScoredSceneNumber = 0;   // 评分失败的任务
            int notStandardSceneNumber = 0;
            int standardSceneNumber = 0;
            double leafSum = 0.0;
            for (TaskEntity task : taskListOfLeafIndex) {
                Double scoreTemp = task.getScore();
                if (scoreTemp == null) {   // 失败状态的任务是没有分数的，计作 0 分。
                    errorSceneNumber++;
                    scoreTemp = 0.0;
                } else if (task.getScored() == null || !task.getScored()) {  // 如果评分失败，也计作 0 分
                    notScoredSceneNumber++;
                    scoreTemp = 0.0;
                } else if (scoreTemp < 100.0) {
                    notStandardSceneNumber++;
                } else if (scoreTemp == 100.0) {
                    standardSceneNumber++;
                }
                // 计算分数总和
                leafSum += scoreTemp;
            }

            // 计算任务的个数
            long taskNumberToScore = taskListOfLeafIndex.size();
            log.debug("项目 " + projectId + " 的叶子指标 " + indexId + " 下参与计算的任务总数为 " + taskNumberToScore + "：仿真异常场景个数 " + errorSceneNumber + "、未达标场景个数 " + notStandardSceneNumber + "、达标场景个数 " + standardSceneNumber);

            // 计算叶子指标得分（任务得分总和 / 任务数量）
            double leafIndexScore = NumberUtil.cut(leafSum / taskNumberToScore, 2);
            // 创建叶子指标对象
            leafIndexTemplate.setTempScore(leafIndexScore);

            LeafIndexEntity leafIndex = LeafIndexEntity.builder().id(StringUtil.getRandomUUID()).pId(projectId).target(leafIndexTemplate.getIndexId()).errorSceneNum(errorSceneNumber).notScoredSceneNum(notScoredSceneNumber).notStandardSceneNum(notStandardSceneNumber).standardSceneNum(standardSceneNumber).score(leafIndexScore).indexId(indexId).parentId(parentId).rootId(rootId).weight(weight).scoreExplain(scoreExplain).packageLevel(packageLevel).build();
            leafIndex.setCreateUserId(projectUserId);
            leafIndex.setCreateTime(TimeUtil.getNowForMysql());
            leafIndex.setModifyUserId(projectUserId);
            leafIndex.setModifyTime(TimeUtil.getNowForMysql());
            leafIndex.setIsDeleted("0");

            leafIndexList.add(leafIndex);
        }
        if (CollectionUtil.isNotEmpty(sceneComplexityEvaluationComputeParam.getSceneEvaluationComputeSubParam())) {
            sceneComplexityEvaluationComputeParam.setSceneEvaluationRuleId(projectEntity.getComplexityEvaluationRuleId());
            sceneComplexityEvaluationComputeParam.setCreateUserId(null);
            sceneComplexityEvaluationComputeParam.setTaskId(projectId);
            sceneComplexityEvaluationComputeParam.setComputeType(DictConstants.COMPLEXITY);
            computeSceneEvaluation(sceneComplexityEvaluationComputeParam);
        }
        if (CollectionUtil.isNotEmpty(sceneRiskEvaluationComputeParam.getSceneEvaluationComputeSubParam())) {
            sceneRiskEvaluationComputeParam.setSceneEvaluationRuleId(projectEntity.getRiskEvaluationRuleId());
            sceneRiskEvaluationComputeParam.setCreateUserId(null);
            sceneRiskEvaluationComputeParam.setTaskId(projectId);
            sceneRiskEvaluationComputeParam.setComputeType(DictConstants.RISK);
            sceneRiskEvaluationComputeParam.setVehicleId(projectEntity.getVehicle());
            sceneRiskEvaluationComputeParam.setAlgorithmId(projectEntity.getAlgorithm());
            computeSceneEvaluation(sceneRiskEvaluationComputeParam);
        }
        // 保存叶子指标得分
        taskIndexRepository.batchInsertLeafIndex(leafIndexList);
        // 保存一级指标分数
        log.debug("项目 " + projectId + " 的所有任务分数为：" + taskList);
        computeFirst(leafIndexList, allIndexTemplateList, projectId, maxLevel);
        log.info("项目 " + projectId + " 打分完成！");
    }

    public void computeSceneEvaluation(SceneEvaluationComputeParam sceneEvaluationComputeParam) {
        sceneEvaluationComputeParam.setLinuxTempPath(linuxTempPath);
        sceneEvaluationComputeParam.setBucketName(bucketName);
        ThreadPool.sceneEvaluationComputePool.execute(new SceneEvaluationComputeRunnable(sceneEvaluationComputeParam));
    }

    public void computeFirst(List<LeafIndexEntity> leafIndexList, List<IndexTemplateEntity> allIndexTemplateList, String projectId, int maxLevel) {
        log.debug("计算父指标得分：" + leafIndexList);
        Iterator<LeafIndexEntity> leafTaskIndexIterator = leafIndexList.iterator();
        // 把 1 级的指标得分直接保存
        while (leafTaskIndexIterator.hasNext()) {
            LeafIndexEntity leafTaskIndex = leafTaskIndexIterator.next();
            if (leafTaskIndex.getPackageLevel() == 1) {
                leafTaskIndex.setCreateUserId(leafTaskIndex.getCreateUserId());
                leafTaskIndex.setCreateTime(TimeUtil.getNowForMysql());
                leafTaskIndex.setModifyUserId(leafTaskIndex.getModifyUserId());
                leafTaskIndex.setModifyTime(TimeUtil.getNowForMysql());
                leafTaskIndex.setIsDeleted("0");
                indexMapper.insertFirstIndex(leafTaskIndex);
                leafTaskIndexIterator.remove();
            }
        }
        if (leafIndexList.size() > 0) {
            List<LeafIndexEntity> nextLevelIndexList = new ArrayList<>();
            // 找出等级和 maxLevel 不相同的指标暂时不计算
            leafIndexList.stream().filter(po -> maxLevel != po.getPackageLevel()).forEach(nextLevelIndexList::add);
            // 找出等级和 maxLevel 相同的指标并根据父指标分组
            Map<String, List<LeafIndexEntity>> sonTaskIndexMap = leafIndexList.stream().filter(po -> maxLevel == po.getPackageLevel()).collect(Collectors.groupingBy(LeafIndexEntity::getParentId));
            Set<String> parentIdSet = sonTaskIndexMap.keySet();
            List<String> parentIdList = CollectionUtil.setToList(parentIdSet);

            List<IndexTemplateEntity> parentIndexTemplateList = allIndexTemplateList.stream().filter(indexTemplate -> parentIdList.contains(indexTemplate.getIndexId())).collect(Collectors.toList());
            // 计算父指标得分
            parentIndexTemplateList.forEach(indexTemplate -> {
                String weight = indexTemplate.getWeight();
                List<LeafIndexEntity> sonTaskIndexList = sonTaskIndexMap.get(indexTemplate.getIndexId());
                double parentScore = NumberUtil.cut(sonTaskIndexList.stream().mapToDouble(taskIndex -> taskIndex.getScore() * Double.parseDouble(taskIndex.getWeight()) / 100).sum(), 2);
                LeafIndexEntity parentTaskIndex = LeafIndexEntity.builder().id(StringUtil.getRandomUUID()).pId(projectId).target(indexTemplate.getIndexId()).score(parentScore).indexId(indexTemplate.getIndexId()).parentId(indexTemplate.getParentId()).rootId(indexTemplate.getRootId()).weight(weight).packageLevel(maxLevel - 1).build();
                nextLevelIndexList.add(parentTaskIndex);
            });
            // 将父指标作为叶子指标递归
            computeFirst(nextLevelIndexList, allIndexTemplateList, projectId, maxLevel - 1);
        }
    }

    @SneakyThrows
    public void evaluationLevel(String projectId) {
        String tokenUrl = customConfiguration.getTokenUri() + "?grant_type=client_credentials" + "&client_id=" + customConfiguration.getClientId() + "&client_secret=" + customConfiguration.getClientSecret();
        log.debug("获取仿真云平台 token：" + tokenUrl);
        String response = HttpUtil.get(tokenUrl);
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonNode = objectMapper.readTree(response);
        String accessToken = jsonNode.path("access_token").asText();
        log.debug("仿真云平台 token 为：" + accessToken);
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", "Bearer " + accessToken);
        Map<String, String> params = new HashMap<>();
        params.put("id", projectId);
        String post = HttpUtil.post(customConfiguration.getEvaluationLevelUri(), headers, params);
        log.info("访问仿真云平台评价等级接口：" + customConfiguration.getEvaluationLevelUri() + "，请求头为：" + headers + "，请求体为：" + params + "结果为：" + post);
    }

    public Boolean taskConfirm(String taskId) {
        String state = simulationManualProjectTaskMapper.selectStateById(taskId);
        return !DictConstants.TASK_COMPLETED.equals(state);
    }

    public void taskTick(String taskId) {
        log.debug("收到任务 " + taskId + " 的心跳。");
        TaskEntity taskEntity = simulationManualProjectTaskMapper.selectById(taskId);
        if (taskEntity == null) {
            return;
        }
        String projectId = taskEntity.getPId();
        String projectUserId = taskEntity.getCreateUserId();
        // 刷新 redis 心跳时间
        PrefixEntity redisPrefix = projectDomainService.getRedisPrefixByUserIdAndProjectIdAndTaskId(projectUserId, projectId, taskId);
        if (RedisUtil.getStringByKey(stringRedisTemplate, redisPrefix.getProjectRunningKey()) != null) {
            stringRedisTemplate.opsForValue().set(redisPrefix.getTaskTickKey(), TimeUtil.getNowString());
        }

    }

    /**
     * 多模式仿真任务刷新心跳的时间redisKey
     */
    public String getMultiTaskTickPodRedisKey(String taskId, String projectId) {
        return "multi_project:" + projectId + ":taskId:" + taskId + ":taskTickPod";
    }


    @SneakyThrows
    public void done(PrefixEntity redisPrefix, String projectId, String projectType) {
        // 更新项目状态为已完成
        if (DictConstants.PROJECT_TYPE_MANUAL.equals(projectType)) {
            simulationManualProjectMapper.updateProjectState(projectId, DictConstants.PROJECT_COMPLETED, TimeUtil.getNowForMysql());
        } else if (DictConstants.PROJECT_TYPE_AUTO_SUB.equals(projectType)) {
            simulationAutomaticSubprojectMapper.updateProjectState(projectId, DictConstants.PROJECT_COMPLETED, TimeUtil.getNowForMysql());
        }
        // 删除 kafka topic
        KafkaUtil.deleteTopic(admin, projectId);
        // 删除 redis 中的 项目运行信息 键值对
        RedisUtil.deleteByPrefix(stringRedisTemplate, redisPrefix.getProjectRunningKey());
        RedisUtil.deleteByPrefix(stringRedisTemplate, "project:" + projectId);
        // 删除剩余 yaml
        projectDomainService.deleteYamlByProjectId(projectId);
        // 查询并保存项目详情
        projectDomainService.selectProjectDetailsById(projectType, projectId);
        // 查询并保存项目报告
        projectDomainService.selectProjectReportById(projectType, projectId);
        // 如果使用算法平台算法则修改状态为测试完成

        projectDomainService.checkAlgorithmIsExpand(projectType, projectId, DictConstants.ALGORITHM_EXPAND_STATUS_TESTED);
    }


    @SneakyThrows
    public List<SceneEntity> getSceneList(String projectId, String packageId) {

        String allIndexPrefix = "project:" + projectId + ":package:" + packageId + ":all";
        String leafIndexPrefix = "project:" + projectId + ":package:" + packageId + ":leaf";
        //1 查询该场景包的所有指标列表，包删了无所谓，但要过滤删掉的指标。并保存成 json 文件。
        List<IndexTemplateEntity> allIndexList = indexTemplateMapper.selectByPackageIdIncludeDeleted(packageId);
        stringRedisTemplate.opsForValue().set(allIndexPrefix, JsonUtil.listToJson(allIndexList));
        //2 查询场景包叶子指标
        List<IndexTemplateEntity> leafIndexList = allIndexList.stream().filter(index -> StringUtil.isNotEmpty(index.getRuleId())).collect(Collectors.toList());
        log.info("项目 " + projectId + " 的叶子指标为：" + leafIndexList);
        stringRedisTemplate.opsForValue().set(leafIndexPrefix, JsonUtil.listToJson(leafIndexList));
        List<SceneEntity> sceneList = new ArrayList<>();
        leafIndexList.forEach(leafIndex -> {
            String naturalIds = leafIndex.getSceneNaturalIds();
            String standardIds = leafIndex.getSceneStatueIds();
            String accidentIds = leafIndex.getSceneTrafficIds();
            String generalizationIds = leafIndex.getSceneGeneralizationIds();
            String sceneReferenceIds = leafIndex.getSceneReferenceLibIds();
            if (StringUtil.isNotEmpty(naturalIds)) {
                List<String> naturalIdList = new ArrayList<>(Arrays.asList(naturalIds.split(",")));
                sceneList.addAll(sceneMapper.selectNaturalByIdList(naturalIdList));
            }
            if (StringUtil.isNotEmpty(standardIds)) {
                List<String> standardIdList = new ArrayList<>(Arrays.asList(standardIds.split(",")));
                sceneList.addAll(sceneMapper.selectStandardByIdList(standardIdList));
            }
            if (StringUtil.isNotEmpty(accidentIds)) {
                List<String> accidentIdList = new ArrayList<>(Arrays.asList(accidentIds.split(",")));
                sceneList.addAll(sceneMapper.selectAccidentByIdList(accidentIdList));
            }
            if (StringUtil.isNotEmpty(generalizationIds)) {
                List<String> generalizationIdList = new ArrayList<>(Arrays.asList(generalizationIds.split(",")));
                sceneList.addAll(sceneMapper.selectGeneralizationByIdList(generalizationIdList));
            }
            if (StringUtil.isNotEmpty(sceneReferenceIds)) {
                List<String> sceneReferenceIdList = new ArrayList<>(Arrays.asList(sceneReferenceIds.split(",")));
                sceneList.addAll(sceneMapper.selectReferenceLibByIdList(sceneReferenceIdList));
            }
        });
        log.info("项目" + projectId + " 共有 " + sceneList.size() + " 个任务：" + sceneList);
        return sceneList;
    }
}
