package com.cn.fdapfe.biz.service.impl;

import com.alibaba.fastjson2.JSON;
import com.cn.fdapfe.biz.domain.Data;
import com.cn.fdapfe.biz.domain.FaultPhysicalModel;
import com.cn.fdapfe.biz.domain.ModelDataGen;
import com.cn.fdapfe.biz.mapper.FaultPhysicalModelMapper;
import com.cn.fdapfe.biz.mapper.ModelDataGenMapper;
import com.cn.fdapfe.biz.service.IDataService;
import com.cn.fdapfe.biz.service.IModelDataGenService;
import com.cn.fdapfe.common.core.domain.AjaxResult;
import com.cn.fdapfe.common.utils.DateUtils;
import com.cn.fdapfe.common.utils.SecurityUtils;
import com.cn.fdapfe.common.utils.file.FileToMultipartFile;
import com.cn.fdapfe.web.controller.common.CommonController;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 物理模型数据生成管理Service业务层处理
 *
 * @author Allen
 * @date 2025-03-07
 */
@Service
public class ModelDataGenServiceImpl implements IModelDataGenService {
    private static final Logger logger = LoggerFactory.getLogger(ModelDataGenServiceImpl.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    // 文件路径验证正则表达式
    private static final Pattern FILE_PATH_PATTERN = Pattern.compile(
            "^(?:[a-zA-Z]:[\\\\/](?:[^\\\\/:*?\"<>|]+[\\\\/])*[^\\\\/:*?\"<>|]*|/[^/:*?\"<>|]*(?:/[^/:*?\"<>|]*)*)$"
    );

    // 模型归属类型常量
    private static final String MODEL_ATTRIBUTION_LOCAL = "0";
    private static final String MODEL_ATTRIBUTION_MANUAL = "2";

    // 数据类型常量
    private static final String DATA_TYPE_PHYSICAL = "0";
    private static final String DATA_TYPE_ACTUAL = "1";
    private static final String DATA_TYPE_NOISE = "3";

    @Resource
    private ModelDataGenMapper modelDataGenMapper;
    @Resource
    private IDataService dataService;
    @Resource
    private FaultPhysicalModelMapper faultPhysicalModelMapper;
    @Resource
    private CommonController commonController;
    @Value("${model.data.storage-path:D:/modelData}")
    private String storagePath;

    @Override
    public List<ModelDataGen> selectList(ModelDataGen po) {
        QueryWrapper<ModelDataGen> wrapper = buildQueryWrapper(po);
        wrapper.orderByDesc("create_time");
        return modelDataGenMapper.selectList(wrapper);
    }

    @Override
    public ModelDataGen selectOne(String id) {
        QueryWrapper<ModelDataGen> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(ModelDataGen::getDataGenId, id);
        return modelDataGenMapper.selectOne(wrapper);
    }

    /**
     * 将指定目录下的指定文件拷贝、重命名并移动到目标文件夹
     * @param sourceDirectory 源文件所在的目录
     * @param sourceFileName 源文件的名称
     * @param targetDirectory 目标文件夹
     * @param newFileName 重命名后的新文件名
     * @throws IOException 如果文件操作过程中出现 I/O 错误
     */
    public static String copyRenameAndMoveFile(String sourceDirectory, String sourceFileName,
                                               String targetDirectory, String newFileName) throws IOException {
        // 构建源文件的路径
        Path sourcePath = Paths.get(sourceDirectory, sourceFileName);
        // 构建目标文件的路径
        Path targetPath = Paths.get(targetDirectory, newFileName);

        // 检查源文件是否存在
        if (Files.exists(sourcePath)) {
            // 创建目标文件夹（如果不存在）
            Files.createDirectories(targetPath.getParent());
            // 拷贝文件并使用新的文件名
            Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            // 可以选择删除源文件以实现移动效果
            Files.delete(sourcePath);

        } else {
            System.out.println("源文件不存在: " + sourcePath);
        }
        return targetPath.toString();
    }
    /**
     * 启动异步任务
     */
    @Override
    public int add(ModelDataGen po) throws Exception {
        FaultPhysicalModel model = faultPhysicalModelMapper.selectById(po.getModelId());
//        po.setModelId(model.getModelName());
        ObjectMapper mapper = new ObjectMapper();
        LinkedHashMap[] array = mapper.readValue(po.getPostApiData(), LinkedHashMap[].class);
        for (LinkedHashMap item : array) {
            Data data = new Data();
            if(model.getModelAttribution().equals("0")){
                try {
                    //写入配置文件信息
                    writeToTxt(item.get("inputPath").toString(),item.get("modelData").toString());
                    //启动模型开始生成
                    ExeProcessManager.startExe(item.get("exePath").toString(),true);
                    //判断是否进行加噪
                    if(item.get("isNoise").toString().equals("true")){
                        //如果加噪，根据模型id查询
                        FaultPhysicalModel noiseModel = faultPhysicalModelMapper.selectById(item.get("noiseModel").toString());
                        HashMap<String, Object> noiseMap = new HashMap<>();
                        noiseMap.put("noiseType",item.get("noiseType").toString());
                        noiseMap.put("input_file",item.get("outputPath").toString().replace("\\", "/"));
                        //调用噪声算法进行加噪
                        sendPostRequest(noiseModel.getModelPath(),JSON.toJSONString(noiseMap));
                        //如果加噪声了数据类型就是噪声数据
                        data.setType("3");
                    }else{
                        data.setType("0");
                    }
                    //没有加噪声了数据类型就是物理模型数据数据
                    //将文件进行备份存储
                    FileToMultipartFile file = new FileToMultipartFile(item.get("outputPath").toString(), "file");
                    AjaxResult ajaxResult = commonController.customUploadFile(file, "D:/modelData");
                    data.setUrl(ajaxResult.get("url").toString());
                    data.setName(po.getDataGenName());
                    data.setFileName(ajaxResult.get("originalFilename").toString());
                    data.setFileSize(ajaxResult.get("fileSize").toString());
                    data.setFileSizeBytes(ajaxResult.get("fileSizeBytes").toString());
                    data.setFileSuffix(ajaxResult.get("fileSuffix").toString());
                    dataService.insertData(data);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }else if(model.getModelAttribution().equals("2")){
                //直接调用噪声，手动上传的为实际数据
                data.setType("1");
                String key = processMap(item);
                HashMap<String, Object> noiseMap = new HashMap<>();
                noiseMap.put("noiseType",item.get("noiseType").toString());
                noiseMap.put(key,item.get(key).toString());
                FaultPhysicalModel noiseModel = faultPhysicalModelMapper.selectById(po.getModelId());
                //调用噪声算法进行加噪
                sendPostRequest(noiseModel.getModelPath(),JSON.toJSONString(noiseMap));
                //将文件进行备份存储
                FileToMultipartFile file = new FileToMultipartFile(item.get("input_file").toString(), "file");
                AjaxResult ajaxResult = commonController.customUploadFile(file, "D:/modelData");
                try {
                    data.setUrl(ajaxResult.get("url").toString());
                    data.setName(po.getDataGenName());
                    data.setFileName(ajaxResult.get("originalFilename").toString());
                    data.setFileSize(ajaxResult.get("fileSize").toString());
                    data.setFileSizeBytes(ajaxResult.get("fileSizeBytes").toString());
                    data.setFileSuffix(ajaxResult.get("fileSuffix").toString());
                    dataService.insertData(data);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return modelDataGenMapper.insert(po);
    }

    /**
     * 启动可执行程序，设置正确的工作目录
     */
    public static void startExe(String exePath) throws IOException, InterruptedException {
        // 获取exe文件所在目录
        File exeFile = new File(exePath);
        File workingDir = exeFile.getParentFile();
        // 打印调试信息
        logger.info("设置工作目录为: {}", workingDir.getAbsolutePath());
        ProcessBuilder processBuilder = new ProcessBuilder(exePath);
        // 设置工作目录为exe所在目录
        processBuilder.directory(workingDir);
        // 重定向错误输出到标准输出，便于调试
        processBuilder.redirectErrorStream(true);
        // 启动进程
        Process process = processBuilder.start();
        // 读取进程输出
        java.io.BufferedReader reader = new java.io.BufferedReader(
                new java.io.InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            logger.info("Exe输出: {}", line);
        }
        int exitCode = process.waitFor();
        logger.info("程序退出码: {}", exitCode);
    }


    /**
     * 查找Map中的文件路径键
     */
    private String processMap(LinkedHashMap<String, Object> map) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            // 检查值是否为字符串且符合文件路径格式
            if (value instanceof String && isValidFilePath((String) value)) {
                return key; // 找到路径参数，返回对应的key
            }
        }

        // 没有找到符合条件的路径参数
        throw new IllegalArgumentException("Map中未找到有效的文件路径参数");
    }
    /**
     * 判断字符串是否为有效的文件路径
     */
    private static boolean isValidFilePath(String path) {
        if (path != null || !path.trim().isEmpty()) {
            return FILE_PATH_PATTERN.matcher(path).matches();
        }
        return false;
    }

    /**
     * 从URL中提取最后一部分
     */
    public static String extractLastPart(String url, String dom) {
        int lastSlashIndex = url.lastIndexOf(dom);
        if (lastSlashIndex != -1 && lastSlashIndex < url.length() - 1) {
            return url.substring(lastSlashIndex + 1);
        }
        return "";
    }

    /**
     * 将内容写入文本文件
     */
    public static void writeToTxt(String filePath, String content) throws IOException {
        try (FileWriter writer = new FileWriter(filePath, false)) {
            writer.write("");
            writer.write(content);
            writer.flush();
        }
    }

    @Override
    public int update(ModelDataGen po) {
        ModelDataGen one = selectOne(po.getDataGenId());
        setCreateInfo(po);
        return one != null ? modelDataGenMapper.updateById(po) : modelDataGenMapper.insert(po);
    }

    @Override
    public int delete(String[] ids) {
        if (ids == null || ids.length == 0) {
            return 0;
        }
        return ids.length == 1 ? modelDataGenMapper.deleteById(ids[0]) : modelDataGenMapper.deleteBatchIds(Arrays.asList(ids));
    }

//    public static void main(String[] args) {
//        http://127.0.0.1:10000/jrzs
//        String apiUrl= "";
//        String jsonBody = "{" +
//               "input_file" : "D:/matlab_data/data_load/output/fea_all.mat," +
//                "noiseType" : +
//                "}"
//    }
    /**
     * 发送POST请求
     */
    public static boolean sendPostRequest(String apiUrl, String jsonBody) throws IOException {
        URL url = new URL(apiUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        // 显式指定UTF-8编码
        connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        connection.setDoOutput(true);

        byte[] requestBody = jsonBody.getBytes(StandardCharsets.UTF_8);
        connection.setRequestProperty("Content-Length", String.valueOf(requestBody.length));

        try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
            outputStream.write(requestBody);
            outputStream.flush();
        }

        int responseCode = connection.getResponseCode();
        StringBuilder response = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(
                        responseCode >= 200 && responseCode < 300 ?
                                connection.getInputStream() : connection.getErrorStream(),
                        StandardCharsets.UTF_8
                )
        )) {
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
        }
        logger.info("API 请求响应: {}", response);
        // 判断响应状态码是否在200 - 299之间
        return responseCode >= 200 && responseCode < 300;
    }

    /**
     * 构建查询条件
     */
    private QueryWrapper<ModelDataGen> buildQueryWrapper(ModelDataGen po) {
        QueryWrapper<ModelDataGen> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.isNotEmpty(po.getDataGenName()), "data_gen_name", po.getDataGenName())
                .eq(StringUtils.isNotEmpty(po.getModelId()), "model_id", po.getModelId())
                .eq(StringUtils.isNotEmpty(po.getStatus()), "status", po.getStatus())
                .eq(StringUtils.isNotEmpty(po.getDataGenId()), "data_gen_id", po.getDataGenId())
                .eq(StringUtils.isNotEmpty(po.getCreateBy()), "create_by", po.getCreateBy());
        if (po.getStartTime() != null) {
            wrapper.ge("start_time", po.getStartTime());
        }
        if (po.getEndTime() != null) {
            wrapper.le("end_time", po.getEndTime());
        }
        return wrapper;
    }

    /**
     * 设置更新信息
     */
    private void setUpdateInfo(ModelDataGen po) {
        po.setUpdateBy(SecurityUtils.getUsername());
        po.setUpdateTime(DateUtils.getNowDate());
    }

    /**
     * 设置创建信息
     */
    private void setCreateInfo(ModelDataGen po) {
        po.setCreateTime(DateUtils.getNowDate());
        po.setCreateBy(SecurityUtils.getUsername());
    }
}
