package com.ruoyi.file.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.file.domain.FileUpdateInfo;
import com.ruoyi.file.domain.FileWithId;
import com.ruoyi.file.mapper.FileUpdateInfoMapper;
import com.ruoyi.file.service.FileUpdateInfoService;
import com.usky.common.core.bean.CommonPage;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class FileUpdateInfoServiceImpl implements FileUpdateInfoService {
    private static final Logger logger = LoggerFactory.getLogger(FileUpdateInfoServiceImpl.class);

    @Autowired
    private FileUpdateInfoMapper fileUpdateInfoMapper;

    @Value("${file.remote-url}")
    private String remoteUrlBase;

    // 从配置文件中读取脚本名称
    @Value("${file.linuxrestart}")
    private String linuxContralScript;

    @Value("${file.windowsstop}")
    private String windowsStopScript;

    @Value("${file.windowsstart}")
    private String windowsStartScript;

    @Override
    public void addFileToDatabase(FileUpdateInfo fileUpdateInfo) throws Exception {
        // 假设前端只传入文件名
        String fileName = fileUpdateInfo.getFileName();
        if (fileName == null || fileName.trim().isEmpty()) {
            throw new IllegalArgumentException("文件名不能为空");
        }

        // 设置文件路径
        String filePath = FileUpdateInfo.getServiceDir() + File.separator + fileUpdateInfo.getFileName();

        File file = new File(filePath);

        if (file.exists()) {
            // 如果文件存在，计算本地文件的 MD5 值
            String fileMd5 = DigestUtils.md5Hex(new FileInputStream(file));
            fileUpdateInfo.setFileMd5(fileMd5);
            fileUpdateInfo.setUpdateStatus(0);
        } else {
            // 如果文件不存在，设置状态为“待更新”
            fileUpdateInfo.setFileMd5(null);
            fileUpdateInfo.setUpdateStatus(1);
        }

        fileUpdateInfo.setVersion("v1.0.0"); // 初始化版本号
        fileUpdateInfo.setCreateTime(LocalDateTime.now());
        fileUpdateInfo.setUpdateTime(null);
        fileUpdateInfoMapper.insert(fileUpdateInfo);
    }

    //检查是否需要更新
    @Override
    public boolean checkFileUpdate(Long id) throws Exception {
        FileUpdateInfo fileUpdateInfo = fileUpdateInfoMapper.selectById(id);
        if (fileUpdateInfo == null) {
            throw new FileNotFoundException("文件信息未找到：id=" + id);
        }

        String localFilePath = fileUpdateInfo.getFilePath(); // 使用新的 getFilePath 方法
        File localFile = new File(localFilePath);

        String remoteFileMd5 = getRemoteFileMd5(fileUpdateInfo.getFileName());

        if (!localFile.exists()) {
            // 如果本地文件不存在，设置状态为“待更新”
            fileUpdateInfo.setRemoteMd5(remoteFileMd5);
            fileUpdateInfo.setUpdateStatus(1); // 设置为待更新
            fileUpdateInfoMapper.updateById(fileUpdateInfo);
            return true; // 需要更新
        } else {
            // 如果本地文件存在，比较 MD5 值
            String localFileMd5 = DigestUtils.md5Hex(new FileInputStream(localFile));
            boolean isUpdateRequired = !localFileMd5.equals(remoteFileMd5);
            fileUpdateInfo.setRemoteMd5(remoteFileMd5);
            fileUpdateInfo.setUpdateStatus(isUpdateRequired ? 1 : 0);  // 0表示无需更新，1表示待更新
            fileUpdateInfoMapper.updateById(fileUpdateInfo);
            return isUpdateRequired;
        }
    }

    //下载更新
    @Override
    public void performFileUpdate(Long id) throws Exception {
        FileUpdateInfo fileUpdateInfo = fileUpdateInfoMapper.selectById(id);
        if (fileUpdateInfo == null) {
            throw new FileNotFoundException("文件信息未找到：id=" + id);
        }

        String localFilePath = fileUpdateInfo.getFilePath(); // 使用新的 getFilePath 方法
        String fileName = fileUpdateInfo.getFileName();
        String remoteUrl = remoteUrlBase + fileName;

        try {
            // 获取 JAR 文件所在的目录
            String serviceDir = FileUpdateInfo.getServiceDir();
            String backupDirPath = serviceDir + File.separator + "backup";
            File backupDir = new File(backupDirPath);
            if (!backupDir.exists()) {
                boolean created = backupDir.mkdirs();
                if (!created) {
                    throw new IOException("无法创建备份目录");
                }
            }

            File originalFile = new File(localFilePath);
            if (originalFile.exists()) {
                String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
                String backupFilePath = backupDirPath + File.separator + fileName + "." + timestamp;
                Files.move(originalFile.toPath(), Paths.get(backupFilePath), StandardCopyOption.REPLACE_EXISTING);
                System.out.println("旧文件已备份到: " + backupFilePath);
            }

            downloadFileFromRemote(remoteUrl, localFilePath);

            String newFileMd5 = DigestUtils.md5Hex(new FileInputStream(localFilePath));
            fileUpdateInfo.setFileMd5(newFileMd5);
            fileUpdateInfo.setCreateTime(fileUpdateInfo.getCreateTime());
            fileUpdateInfo.setUpdateTime(LocalDateTime.now());
            fileUpdateInfo.setUpdateStatus(0);

            // 递增版本号
            String currentVersion = fileUpdateInfo.getVersion();
            String[] versionParts = currentVersion.substring(1).split("\\."); // 去掉 'v' 并拆分版本号
            int major = Integer.parseInt(versionParts[0]);
            int minor = Integer.parseInt(versionParts[1]);
            int patch = Integer.parseInt(versionParts[2]) + 1; // 小版本号加 1
            fileUpdateInfo.setVersion("v" + major + "." + minor + "." + patch);

            fileUpdateInfoMapper.updateById(fileUpdateInfo);

            // 下载完成后重启服务
            controlApplication(fileName, "restart");

        } catch (FileNotFoundException e) {
            throw new Exception("原因: " + e.getMessage(), e);
        } catch (IOException e) {
            throw new Exception("原因: " + e.getMessage(), e);
        } catch (Exception e) {
            throw new Exception("原因: 远程仓库未找到服务包");
        }
    }

    private void downloadFileFromRemote(String remoteUrl, String localFilePath) throws Exception {
        URL url = new URL(remoteUrl);
        Path path = Paths.get(localFilePath);
        try {
            Files.copy(url.openStream(), path, StandardCopyOption.REPLACE_EXISTING);
            System.out.println("文件下载成功");
        } catch (IOException e) {
            System.err.println("文件下载失败" + "，原因: " + e.getMessage());
            throw new Exception("文件下载失败");
        }
    }

    private String getRemoteFileMd5(String fileName) throws Exception {
        String remoteUrl = remoteUrlBase + fileName;
        System.out.println("远程文件 URL: " + remoteUrl);
        URL url = new URL(remoteUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestProperty("Cache-Control", "no-cache");
        try (InputStream in = connection.getInputStream()) {
            return DigestUtils.md5Hex(in);
        }
    }

    @Override
    public CommonPage<FileUpdateInfo> getAllFiles(int current, int size) throws Exception {
        // 使用 MyBatis-Plus 的分页功能
        Page<FileUpdateInfo> page = new Page<>(current, size);
        IPage<FileUpdateInfo> result = fileUpdateInfoMapper.selectPage(page, null);

        // 更新每个文件的运行状态
        for (FileUpdateInfo fileUpdateInfo : result.getRecords()) {
            checkServiceStatus(fileUpdateInfo);
        }

        // 将 MyBatis-Plus 的分页结果转换为 CommonPage
        return new CommonPage<>(
                result.getRecords(),
                (int) result.getTotal(),
                size,
                current
        );
    }

    @Override
    public CommonPage<FileUpdateInfo> getFilesByFileNameContaining(String fileName, int current, int size) throws Exception {
        // 使用 MyBatis-Plus 的分页功能
        Page<FileUpdateInfo> page = new Page<>(current, size);
        QueryWrapper<FileUpdateInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("file_name", fileName);
        IPage<FileUpdateInfo> result = fileUpdateInfoMapper.selectPage(page, queryWrapper);

        // 更新每个文件的运行状态
        for (FileUpdateInfo fileUpdateInfo : result.getRecords()) {
            checkServiceStatus(fileUpdateInfo);
        }

        // 将 MyBatis-Plus 的分页结果转换为 CommonPage
        return new CommonPage<>(
                result.getRecords(),
                (int) result.getTotal(),
                size,
                current
        );
    }

    @Override
    public FileUpdateInfo getFileById(Long id) {
        return fileUpdateInfoMapper.selectById(id);
    }

    @Override
    public void deleteFileById(Long id) {
        fileUpdateInfoMapper.deleteById(id);
    }

    @Override
    public void controlApplication(String fileName, String action) throws Exception {
        String osName = System.getProperty("os.name").toLowerCase();

        if (osName.contains("linux") || osName.contains("unix")) {
            // Linux 系统
            String command = "sh " + linuxContralScript + " " + action + " " + fileName;
            Runtime.getRuntime().exec(command);
        } else if (osName.contains("windows")) {
            // Windows 系统
            String command;
            switch (action.toLowerCase()) {
                case "start":
                    command = windowsStartScript + " " + fileName;
                    break;
                case "stop":
                    command = windowsStopScript + " " + fileName;
                    break;
                case "restart":
                    // Windows 系统没有单独的重启脚本，需要先停止再启动
                    Runtime.getRuntime().exec(windowsStopScript + " " + fileName);
                    Thread.sleep(3000); // 等待 3 秒，确保服务停止
                    Runtime.getRuntime().exec(windowsStartScript + " " + fileName);
                    return;
                default:
                    throw new IllegalArgumentException("不支持的操作类型: " + action);
            }
            Runtime.getRuntime().exec(command);
        } else {
            throw new Exception("不支持的操作系统: " + osName);
        }

        // 等待操作完成
        long startTime = System.currentTimeMillis();
        long timeout = 8000; // 最长等待时间 8 秒
        boolean isActionCompleted = false;

        while (System.currentTimeMillis() - startTime < timeout) {
            int currentStatus = getFileStatus(fileName); // 获取当前服务状态
            if (action.equals("start") && currentStatus == 1) {
                isActionCompleted = true;
                break; // 服务已启动
            } else if (action.equals("stop") && currentStatus == 2) {
                isActionCompleted = true;
                break; // 服务已停止
            } else if (action.equals("restart") && currentStatus == 1) {
                isActionCompleted = true;
                break; // 服务已重启
            }

            Thread.sleep(500); // 每隔 500 毫秒检测一次状态
        }

        if (!isActionCompleted) {
            throw new Exception("操作超时，服务状态未达到预期");
        }
    }

    //扫描服务所在目录下的文件
    @Override
    public CommonPage<FileWithId> scanFilesInServiceDir(int current, int size) throws Exception {
        // 获取 JAR 文件所在的目录
        String serviceDir = FileUpdateInfo.getServiceDir();
        File dir = new File(serviceDir);

        if (!dir.exists() || !dir.isDirectory()) {
            throw new Exception("无法找到服务目录: " + serviceDir);
        }

        // 获取目录中的所有文件名
        File[] files = dir.listFiles();
        if (files == null) {
            throw new Exception("无法读取目录内容: " + serviceDir);
        }

        // 提取文件名
        List<String> fileNames = new ArrayList<>();
        for (File file : files) {
            if (file.isFile()) {
                fileNames.add(file.getName());
            }
        }

        // 查询数据库中已存在的文件名
        List<String> existingFileNames = fileUpdateInfoMapper.selectList(null).stream()
                .map(FileUpdateInfo::getFileName)
                .collect(Collectors.toList());

        // 从扫描结果中排除已存在的文件名
        List<String> newFileNames = fileNames.stream()
                .filter(fileName -> !existingFileNames.contains(fileName))
                .collect(Collectors.toList());

        // 对文件名列表进行分页处理
        int total = newFileNames.size();
        int start = (current - 1) * size;
        int end = Math.min(start + size, total);

        List<String> pageFileNames = newFileNames.subList(start, end);

        // 创建包含 id 的文件信息列表
        List<FileWithId> fileWithIds = new ArrayList<>();
        for (int i = 0; i < pageFileNames.size(); i++) {
            fileWithIds.add(new FileWithId((long) (start + i + 1), pageFileNames.get(i)));
        }

        // 返回分页结果
        return new CommonPage<>(fileWithIds, total, size, current);
    }


    // 检测服务运行状态并更新到数据库
    private void checkServiceStatus(FileUpdateInfo fileUpdateInfo) {
        String fileName = fileUpdateInfo.getFileName();
        int fileStatus = getFileStatus(fileName); // 调用方法获取文件运行状态
        fileUpdateInfo.setFileStatus(fileStatus); // 更新运行状态
        fileUpdateInfoMapper.updateById(fileUpdateInfo); // 更新数据库
    }

    // 获取文件运行状态
    @Override
    public int getFileStatus(String fileName) {
        String osName = System.getProperty("os.name").toLowerCase();

        if (osName.contains("linux") || osName.contains("unix")) {
            // Linux 系统
            String command = "pgrep -f " + fileName;
            try {
                Process process = Runtime.getRuntime().exec(command);
                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                String line;
                boolean isRunning = false;
                while ((line = reader.readLine()) != null) {
                    if (line != null && !line.isEmpty()) {
                        isRunning = true;
                        break;
                    }
                }
                return isRunning ? 1 : 2; // 1: 运行中，2: 已停止
            } catch (IOException e) {
                logger.error("Failed to execute command: {}", command, e);
            }
        } else if (osName.contains("windows")) {
            // Windows 系统
            String command = "sc query " + fileName;
            try {
                Process process = Runtime.getRuntime().exec(command);
                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                String line;
                boolean isRunning = false;
                while ((line = reader.readLine()) != null) {
                    if (line.contains("RUNNING")) {
                        isRunning = true;
                        break;
                    } else if (line.contains("STOPPED")) {
                        break;
                    }
                }
                return isRunning ? 1 : 2; // 1: 运行中，2: 已停止
            } catch (IOException e) {
                logger.error("Failed to execute command: {}", command, e);
            }
        } else {
            logger.warn("Unsupported operating system: {}", osName);
        }
        return 0; // 未知
    }
}