package com.css.simulation.resource.scheduler.infra.runnable;

import api.common.pojo.constants.DictConstants;
import api.common.pojo.param.scene.SceneEvaluationComputeParam;
import api.common.pojo.param.scene.SceneEvaluationComputeSubParam;
import api.common.pojo.po.scene.SceneComplexityPO;
import api.common.pojo.po.scene.SceneEvaluationRulePO;
import api.common.pojo.po.scene.SceneRiskPO;
import api.common.util.FileUtil;
import api.common.util.StringUtil;
import api.common.util.TimeUtil;
import com.alibaba.druid.util.StringUtils;
import com.css.simulation.resource.scheduler.adapter.entity.impl.ApplicationContextAwareImpl;
import com.css.simulation.resource.scheduler.infra.db.mysql.mapper.SceneComplexityMapper;
import com.css.simulation.resource.scheduler.infra.db.mysql.mapper.SceneEvaluationRuleMapper;
import com.css.simulation.resource.scheduler.infra.db.mysql.mapper.SceneRiskMapper;
import com.css.simulation.resource.scheduler.infra.fs.minio.MinioUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.minio.MinioClient;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.HashSet;
import java.util.Set;

import static api.common.pojo.enums.SceneEvaluationEnum.matchLevelEnumByLevel;

/**
 * @author 15765
 * @date 2024/4/10 22:22
 */
@Slf4j
public class SceneEvaluationComputeRunnable implements Runnable {

    private final SceneEvaluationComputeParam sceneEvaluationComputeParam;

    public SceneEvaluationComputeRunnable(SceneEvaluationComputeParam sceneEvaluationComputeParam) {
        this.sceneEvaluationComputeParam = sceneEvaluationComputeParam;
    }

    @Override
    public void run() {
        String ruleId = sceneEvaluationComputeParam.getSceneEvaluationRuleId();
        log.info("开始执行线程：" + sceneEvaluationComputeParam);
        String bucketName = sceneEvaluationComputeParam.getBucketName();
        SceneEvaluationRuleMapper sceneEvaluationRuleMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneEvaluationRuleMapper.class);
        SceneComplexityMapper sceneComplexityMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneComplexityMapper.class);
        SceneRiskMapper sceneRiskMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneRiskMapper.class);
        MinioClient minioClient = ApplicationContextAwareImpl.getApplicationContext().getBean(MinioClient.class);
        // 获取场景评价规则
        SceneEvaluationRulePO sceneEvaluationRulePO = sceneEvaluationRuleMapper.querySceneEvaluationPyById(ruleId);
        if (sceneEvaluationRulePO == null) {
            log.error(ruleId + " 的场景评价规则已删除");
            return;
        }
        log.info("场景评价规则为：" + sceneEvaluationRulePO);
        // 1 判断有没有用户目录，没有则复制
        String evaluationDirectoryOfUser = sceneEvaluationComputeParam.getLinuxTempPath() + "scene/evaluation/" + sceneEvaluationComputeParam.getTaskId() + "/";
        String scriptsPath = evaluationDirectoryOfUser + "scripts";
        if (!new File(evaluationDirectoryOfUser).exists()) {
            // 1 将场景评价规则脚本保存到 script 目录
            FileUtil.createDirectory(scriptsPath);
        }

        // 下载场景评价脚本到脚本目录
        if (sceneEvaluationRulePO.getScriptPath() == null) {
            return;
        }
        String pyMainPath = scriptsPath + "/" + sceneEvaluationRulePO.getRuleId();
        if (!new File(pyMainPath).exists()) {
            try {
                MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationRulePO.getScriptPath(), pyMainPath);
                Path path = Paths.get(pyMainPath);
                Set<PosixFilePermission> permissions = new HashSet<>();
                permissions.add(PosixFilePermission.OWNER_READ);
                permissions.add(PosixFilePermission.OWNER_WRITE);
                permissions.add(PosixFilePermission.OWNER_EXECUTE);
                permissions.add(PosixFilePermission.GROUP_READ);
                permissions.add(PosixFilePermission.GROUP_WRITE);
                permissions.add(PosixFilePermission.GROUP_EXECUTE);
                permissions.add(PosixFilePermission.OTHERS_READ);
                permissions.add(PosixFilePermission.OTHERS_WRITE);
                permissions.add(PosixFilePermission.OTHERS_EXECUTE);
                Files.setPosixFilePermissions(path, permissions);
            } catch (IOException e) {
                log.error("下载执行文件失败： " + sceneEvaluationRulePO.getScriptPath(), e);
                return;
            }
        }
        String scenePathFather = evaluationDirectoryOfUser + "compScene/";
        String riskPathFather = evaluationDirectoryOfUser + "riskScene/";
        for (SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam : sceneEvaluationComputeParam.getSceneEvaluationComputeSubParam()) {
            if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
                // 创建场景路径
                if (StringUtil.isEmpty(sceneEvaluationComputeSubParam.getSceneXODRPath())
                        || StringUtil.isEmpty(sceneEvaluationComputeSubParam.getSceneXOSCPath())) {
                    continue;
                }
                String scenePath = scenePathFather + sceneEvaluationComputeSubParam.getSceneId();
                if (!new File(scenePath).exists()) {
                    log.info("创建复杂度文件夹：" + scenePath);
                    FileUtil.createDirectory(scenePath);
                }
                try {
                    // 计算复杂度，根据场景 id 获取场景信息，下载 osc odr
                    String scenarioOsc = sceneEvaluationComputeSubParam.getSceneXOSCPath();
                    String[] splitXosc = scenarioOsc.split("/");
                    String xoscName = splitXosc[splitXosc.length - 1];
                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getSceneXOSCPath(), scenePath + "/" + xoscName);

                    String scenarioOdr = sceneEvaluationComputeSubParam.getSceneXODRPath();
                    String[] splitXodr = scenarioOdr.split("/");
                    String xodrName = splitXodr[splitXodr.length - 1];
                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getSceneXODRPath(), scenePath + "/" + xodrName);
                } catch (Exception e) {
                    log.error("文件下载失败", e);
                    FileUtil.deleteFolder(scenePathFather);   // 删除临时文件
                }
            } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.RISK)) {
                if (StringUtil.isEmpty(sceneEvaluationComputeSubParam.getEvaluationPath())) {
                    continue;
                }
                // 创建场景路径
                String scenePath = riskPathFather + sceneEvaluationComputeSubParam.getSceneId();
                if (!new File(scenePath).exists()) {
                    log.info("创建危险度文件夹：" + scenePath);
                    FileUtil.createDirectory(scenePath);
                }
                try {
                    // 计算危险度 从 minio path 下载 csv （ego 和 sensors）
                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getEvaluationPath() + "/Ego.csv", scenePath + "/Ego.csv");
                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getEvaluationPath() + "/evaluation.csv", scenePath + "/evaluation.csv");
                } catch (Exception e) {
                    log.error("文件下载失败", e);
                    FileUtil.deleteFolder(riskPathFather);   // 删除临时文件
                }
            } else {
                return;
            }
        }
        String sceneEvaluationCommand;
        if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
            sceneEvaluationCommand = pyMainPath + " " + scenePathFather + " complexity";
        } else {
            sceneEvaluationCommand = pyMainPath + " " + riskPathFather + " criticality";
        }
        String sceneEvaluationResult;
        log.info("开始执行场景评价命令：" + sceneEvaluationCommand);
        Runtime r = Runtime.getRuntime();
        Process p;
        try {
            p = r.exec(sceneEvaluationCommand);
        } catch (IOException e) {
            log.error("执行场景评价脚本失败，脚本命令为： " + sceneEvaluationCommand, e);
            return;
        }

        StringBuilder sb = new StringBuilder();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String inline;
            while ((inline = br.readLine()) != null) {
                sb.append(inline).append("\n");
            }
            br.close();
        } catch (IOException e) {
            log.error("获取场景评价脚本返回内容失败", e);
            return;
        }
        sceneEvaluationResult = sb.toString();
        log.info("场景评价结束，结果为：" + sceneEvaluationResult);
        for (SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam : sceneEvaluationComputeParam.getSceneEvaluationComputeSubParam()) {
            // 读文件
            StringBuilder result = new StringBuilder();
            try {
                String readPath;
                if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
                    readPath = scenePathFather;
                } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.RISK)) {
                    readPath = riskPathFather;
                } else {
                    return;
                }
                FileInputStream fileInputStream = new FileInputStream(readPath + sceneEvaluationComputeSubParam.getSceneId() + "/scenario_evaluation.json");
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));

                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    result.append(line).append("\n");
                }
                bufferedReader.close();
            } catch (IOException e) {
                log.error("读取场景评价结果失败", e);
                continue;
            }
            String resultStr = result.toString();
            String replace = StringUtil.replace(resultStr, "'", "\"");
            JsonNode rootNode;
            try {
                ObjectMapper mapper = new ObjectMapper();
                //JSON ----> JsonNode
                rootNode = mapper.readTree(replace);

                if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
                    String complexity = rootNode.path("复杂度").asText();
                    String complexityLevel = rootNode.path("复杂度等级").asText();
                    SceneComplexityPO sceneComplexityPO = new SceneComplexityPO();
                    sceneComplexityPO.setSceneId(sceneEvaluationComputeSubParam.getSceneId());
                    sceneComplexityPO.setComplexityId(StringUtil.getRandomUUID());
                    sceneComplexityPO.setSceneType(sceneEvaluationComputeSubParam.getSceneType());
                    sceneComplexityPO.setRuleId(ruleId);
                    sceneComplexityPO.setTaskId(sceneEvaluationComputeParam.getTaskId());
                    sceneComplexityPO.setComplexity(complexity);
                    sceneComplexityPO.setComplexityLevel(matchLevelEnumByLevel(complexityLevel));
                    sceneComplexityPO.setIsDeleted(DictConstants.IS_NOT_DELETED);
                    sceneComplexityPO.setCreateUserId(sceneEvaluationComputeParam.getCreateUserId());
                    sceneComplexityPO.setCreateTime(TimeUtil.getNowForMysql());
                    sceneComplexityMapper.saveSceneComplexity(sceneComplexityPO);
                } else {
                    String risk = rootNode.path("危险度").asText();
                    String riskLevel = rootNode.path("危险度等级").asText();
                    SceneRiskPO sceneRiskPO = new SceneRiskPO();
                    sceneRiskPO.setSceneId(sceneEvaluationComputeSubParam.getSceneId());
                    sceneRiskPO.setRiskId(StringUtil.getRandomUUID());
                    sceneRiskPO.setSceneType(sceneEvaluationComputeSubParam.getSceneType());
                    sceneRiskPO.setRuleId(ruleId);
                    sceneRiskPO.setTaskId(sceneEvaluationComputeParam.getTaskId());
                    sceneRiskPO.setRisk(risk);
                    sceneRiskPO.setRiskLevel(matchLevelEnumByLevel(riskLevel));
                    sceneRiskPO.setIsDeleted(DictConstants.IS_NOT_DELETED);
                    sceneRiskPO.setCreateUserId(sceneEvaluationComputeParam.getCreateUserId());
                    sceneRiskPO.setAlgorithmId(sceneEvaluationComputeParam.getAlgorithmId());
                    sceneRiskPO.setVehicleId(sceneEvaluationComputeParam.getVehicleId());
                    sceneRiskPO.setCreateTime(TimeUtil.getNowForMysql());
                    sceneRiskMapper.saveSceneRisk(sceneRiskPO);
                }
            } catch (Exception e) {
                log.error("场景" + sceneEvaluationComputeSubParam.getSceneId() + " 的场景评价失败：", e);
            }
        }
        // 删除临时文件
//        FileUtil.deleteFolder(sceneEvaluationComputeParam.getLinuxTempPath() + "scene/evaluation/" + sceneEvaluationComputeParam.getTaskId());   // 删除临时文件
    }
}
