package com.ruoyi.app.utils;

import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import net.coobird.thumbnailator.Thumbnails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;

/**
 * 图片压缩工具类 - 使用Thumbnailator实现
 *
 * @author ruoyi
 */
public class ImageCompressUtils {

    private static final Logger log = LoggerFactory.getLogger(ImageCompressUtils.class);

    /**
     * 目标文件大小（200KB）
     */
    private static final long TARGET_SIZE = 100 * 1024; // 200KB = 200 * 1024 字节

    /**
     * 压缩图片 - 根据Web访问路径
     *
     * @param webPath Web访问路径 (如: /profile/upload/2025/08/05/image.jpg)
     * @return 压缩后的Web访问路径，如果不需要压缩则返回原路径
     */
    public static String compressImageByWebPath(String webPath) {
        log.info("开始处理Web路径图片压缩: {}", webPath);
        System.out.println("开始处理Web路径图片压缩: " + webPath);

        if (StringUtils.isEmpty(webPath)) {
            log.warn("Web路径为空，直接返回");
            System.out.println("Web路径为空，直接返回");
            return webPath;
        }

        try {
            // 将Web路径转换为本地文件路径
            String localPath = convertWebPathToLocalPath(webPath);
            log.debug("Web路径转换为本地路径: {} -> {}", webPath, localPath);
            System.out.println("Web路径转换为本地路径: " + webPath + " -> " + localPath);

            if (StringUtils.isEmpty(localPath)) {
                log.warn("无法转换Web路径到本地路径");
                System.out.println("无法转换Web路径到本地路径");
                return webPath;
            }

            File originalFile = new File(localPath);
            if (!originalFile.exists() || !originalFile.isFile()) {
                log.warn("文件不存在或不是文件: {}", localPath);
                System.out.println("文件不存在或不是文件: " + localPath);
                return webPath;
            }

            // 如果文件小于等于200KB，直接返回原路径
            if (originalFile.length() <= TARGET_SIZE) {
                log.info("文件大小 {} 字节，小于等于目标大小，无需压缩", originalFile.length());
                System.out.println("文件大小 " + originalFile.length() + " 字节，小于等于目标大小，无需压缩");
                return webPath;
            }

            // 生成压缩后的本地文件路径
            String compressedLocalPath = generateCompressedLocalPath(localPath);
            log.debug("生成压缩后本地路径: {}", compressedLocalPath);
            System.out.println("生成压缩后本地路径: " + compressedLocalPath);

            // 执行压缩
            log.info("开始压缩图片: {} -> {}", localPath, compressedLocalPath);
            System.out.println("开始压缩图片: " + localPath + " -> " + compressedLocalPath);

            if (compressImageWithThumbnailator(localPath, compressedLocalPath)) {
                // 将压缩后的本地路径转换为Web访问路径
                String compressedWebPath = convertLocalPathToWebPath(compressedLocalPath);
//                String compressedWebPath = compressedLocalPath;
                log.info("图片压缩成功: {} -> {}", webPath, compressedWebPath);
                System.out.println("图片压缩成功: " + webPath + " -> " + compressedWebPath);
                return compressedWebPath;
            } else {
                log.warn("图片压缩失败，返回原始路径: {}", webPath);
                System.out.println("图片压缩失败，返回原始路径: " + webPath);
                return webPath;
            }

        } catch (Exception e) {
            log.error("处理Web路径图片压缩时发生异常: " + webPath, e);
            System.err.println("处理Web路径图片压缩时发生异常: " + webPath + ", 错误: " + e.getMessage());
            e.printStackTrace();
            return webPath;
        } finally {
            // 建议垃圾回收
            System.gc();
        }
    }

    /**
     * 压缩图片 - 根据本地文件路径
     *
     * @param localPath 本地文件路径
     * @return 压缩后的本地文件路径，如果不需要压缩则返回原路径
     */
    public static String compressImageByLocalPath(String localPath) {
        log.info("开始处理本地路径图片压缩: {}", localPath);
        System.out.println("开始处理本地路径图片压缩: " + localPath);

        if (StringUtils.isEmpty(localPath)) {
            log.warn("本地路径为空，直接返回");
            System.out.println("本地路径为空，直接返回");
            return localPath;
        }

        File originalFile = new File(localPath);
        if (!originalFile.exists() || !originalFile.isFile()) {
            log.warn("文件不存在或不是文件: {}", localPath);
            System.out.println("文件不存在或不是文件: " + localPath);
            return localPath;
        }

        // 如果文件小于等于200KB，直接返回原路径
        if (originalFile.length() <= TARGET_SIZE) {
            log.info("文件大小 {} 字节，小于等于目标大小 {} 字节，无需压缩", originalFile.length(), TARGET_SIZE);
            System.out.println("文件大小 " + originalFile.length() + " 字节，小于等于目标大小 " + TARGET_SIZE + " 字节，无需压缩");
            return localPath;
        }

        try {
            // 生成压缩后的文件路径
            String compressedPath = generateCompressedLocalPath(localPath);
            log.debug("生成压缩后路径: {}", compressedPath);
            System.out.println("生成压缩后路径: " + compressedPath);

            // 执行压缩
            log.info("开始压缩图片: {} -> {}", localPath, compressedPath);
            System.out.println("开始压缩图片: " + localPath + " -> " + compressedPath);

            if (compressImageWithThumbnailator(localPath, compressedPath)) {
                log.info("本地图片压缩成功: {} -> {}", localPath, compressedPath);
                System.out.println("本地图片压缩成功: " + localPath + " -> " + compressedPath);
                return compressedPath;
            } else {
                log.warn("本地图片压缩失败，返回原始路径: {}", localPath);
                System.out.println("本地图片压缩失败，返回原始路径: " + localPath);
                return localPath;
            }

        } catch (Exception e) {
            log.error("处理本地路径图片压缩时发生异常: " + localPath, e);
            System.err.println("处理本地路径图片压缩时发生异常: " + localPath + ", 错误: " + e.getMessage());
            e.printStackTrace();
            return localPath;
        } finally {
            System.gc();
        }
    }

    /**
     * 压缩图片 - 简单方法，传入图片路径，返回压缩后的路径
     *
     * @param imagePath 图片路径
     * @return 压缩后的图片路径，如果不需要压缩则返回原路径
     */
    public static String compressImage(String imagePath) {
        log.info("开始压缩图片: {}", imagePath);
        System.out.println("开始压缩图片: " + imagePath);

        if (StringUtils.isEmpty(imagePath)) {
            log.warn("图片路径为空");
            System.out.println("图片路径为空");
            return imagePath;
        }

        // 检查文件是否存在
        File imageFile = new File(imagePath);
        if (!imageFile.exists() || !imageFile.isFile()) {
            log.warn("图片文件不存在: {}", imagePath);
            System.out.println("图片文件不存在: " + imagePath);
            return imagePath;
        }

        // 获取文件大小
        long fileSize = imageFile.length();
        long fileSizeKB = fileSize / 1024;
        log.info("图片文件大小: {} 字节 ({} KB)", fileSize, fileSizeKB);
        System.out.println("图片文件大小: " + fileSize + " 字节 (" + fileSizeKB + " KB)");

        // 如果文件小于等于200KB，直接返回原路径
        if (fileSize <= TARGET_SIZE) {
            log.info("文件大小 {} KB，小于等于200KB，无需压缩", fileSizeKB);
            System.out.println("文件大小 " + fileSizeKB + " KB，小于等于200KB，无需压缩");
            return imagePath;
        }

        log.info("文件大小 {} KB，大于200KB，开始压缩", fileSizeKB);
        System.out.println("文件大小 " + fileSizeKB + " KB，大于200KB，开始压缩");

        // 调用本地路径压缩方法
        return compressImageByLocalPath(imagePath);
    }

    /**
     * 将Web访问路径转换为本地文件路径
     *
     * @param webPath Web路径 (如: /profile/upload/2025/08/05/image.jpg)
     * @return 本地文件路径 (如: /www/wwwroot/server/uploadPath/upload/2025/08/05/image.jpg)
     */
    private static String convertWebPathToLocalPath(String webPath) {
        log.debug("转换Web路径到本地路径: {}", webPath);
        System.out.println("转换Web路径到本地路径: " + webPath);

        if (StringUtils.isEmpty(webPath)) {
            return null;
        }

        // 检查是否是有效的资源路径
        if (!webPath.startsWith(Constants.RESOURCE_PREFIX + "/")) {
            log.warn("不是有效的资源路径: {}", webPath);
            System.out.println("不是有效的资源路径: " + webPath);
            return null;
        }

        // 移除资源前缀，获取相对路径
        String relativePath = webPath.substring(Constants.RESOURCE_PREFIX.length() + 1);
        log.debug("相对路径: {}", relativePath);
        System.out.println("相对路径: " + relativePath);

        // 策略1: 使用配置的profile路径
        String profilePath = RuoYiConfig.getProfile();

            String localPath = Paths.get(profilePath, relativePath).toString();
            log.debug("策略1 - 使用profile路径: {} -> {}", profilePath, localPath);
            System.out.println("策略1 - 使用profile路径: " + profilePath + " -> " + localPath);

            // 检查文件是否存在
            File file = new File(localPath);
            if (file.exists()) {
                log.debug("策略1成功 - 文件存在: {}", localPath);
                System.out.println("策略1成功 - 文件存在: " + localPath);
                return localPath;
            } else {
                log.debug("策略1失败 - 文件不存在: {}", localPath);
                System.out.println("策略1失败 - 文件不存在: " + localPath);
            }
            return null;
    }

    /**
     * 将本地文件路径转换为Web访问路径
     *
     * @param localPath 本地文件路径
     * @return Web访问路径
     */
    private static String convertLocalPathToWebPath(String localPath) {
        // 步骤1：统一替换路径分隔符为正斜杠（兼容所有系统）
        if (StringUtils.isEmpty(localPath)) return null;
        String normalizedPath = localPath.replace("\\", "/");  // 关键点1：统一使用/

        log.debug("转换本地路径到Web路径: {}", normalizedPath);
        String profilePath = RuoYiConfig.getProfile();
        if (profilePath != null) {
            profilePath = profilePath.replace("\\", "/");  // 关键点2：配置路径也统一
        }

        // 步骤2：优化路径匹配逻辑（使用统一的分隔符）
        String webPath = null;
        // 策略1: 检查配置目录
        if (profilePath != null && normalizedPath.startsWith(profilePath)) {
            int startIndex = profilePath.endsWith("/") ?
                    profilePath.length() :
                    profilePath.length() + 1;
            String relativePath = normalizedPath.substring(startIndex);
            webPath = Constants.RESOURCE_PREFIX + "/" + relativePath;
            log.debug("策略1成功 - Web路径: {}", webPath);
        }
        // 策略2: 处理含upload的路径
        else if (normalizedPath.contains("/upload/")) {
            int uploadIndex = normalizedPath.indexOf("/upload/") + 1;
            webPath = Constants.RESOURCE_PREFIX + "/" + normalizedPath.substring(uploadIndex);
            log.debug("策略2成功 - Web路径: {}", webPath);
        }
        // 策略3: 兜底方案（文件名直用）
        else {
            int lastSlash = normalizedPath.lastIndexOf("/");
            String fileName = (lastSlash != -1) ?
                    normalizedPath.substring(lastSlash + 1) :
                    normalizedPath;
            webPath = Constants.RESOURCE_PREFIX + "/upload/" + fileName;
            log.debug("策略3成功 - Web路径: {}", webPath);
        }

        // 步骤3：确保结果有效性
        if (webPath != null) {
            log.info("转换成功: {} → {}", normalizedPath, webPath);
            return webPath;
        } else {
            log.error("转换失败: {}", normalizedPath);
            return null;
        }
    }

    /**
     * 使用Thumbnailator压缩图片
     *
     * @param inputPath  输入路径
     * @param outputPath 输出路径
     * @return 是否压缩成功
     */
    private static boolean compressImageWithThumbnailator(String inputPath, String outputPath) {
        log.info("使用Thumbnailator压缩图片: {} -> {}", inputPath, outputPath);
        System.out.println("使用Thumbnailator压缩图片: " + inputPath + " -> " + outputPath);

        try {
            // 确保输出目录存在
            File outputFile = new File(outputPath);
            File parentDir = outputFile.getParentFile();
            if (!parentDir.exists()) {
                log.debug("创建输出目录: {}", parentDir.getAbsolutePath());
                System.out.println("创建输出目录: " + parentDir.getAbsolutePath());
                parentDir.mkdirs();
            }

            // 使用二分法查找合适的压缩质量
            log.debug("查找最优压缩质量");
            System.out.println("查找最优压缩质量");
            float quality = findOptimalQuality(inputPath, outputPath);

            // 执行最终压缩
            log.debug("执行最终压缩，质量: {}", quality);
            System.out.println("执行最终压缩，质量: " + quality);
            Thumbnails.of(inputPath)
                    .scale(1.0f) // 保持原尺寸
                    .outputQuality(quality) // 设置压缩质量
                    .toFile(outputPath);

            // 检查压缩后的文件大小
            File compressedFile = new File(outputPath);
            boolean success = compressedFile.exists() && compressedFile.length() <= TARGET_SIZE;
            log.info("压缩完成，文件存在: {}, 文件大小: {} 字节, 压缩成功: {}",
                    compressedFile.exists(), compressedFile.length(), success);
            System.out.println("压缩完成，文件存在: " + compressedFile.exists() +
                    ", 文件大小: " + compressedFile.length() + " 字节, 压缩成功: " + success);
            return success;

        } catch (IOException e) {
            log.error("压缩图片时发生IO异常: " + inputPath, e);
            System.err.println("压缩图片时发生IO异常: " + inputPath + ", 错误: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 使用二分法查找最优压缩质量
     */
    private static float findOptimalQuality(String inputPath, String outputPath) {
        log.debug("使用二分法查找最优压缩质量: {}", inputPath);
        System.out.println("使用二分法查找最优压缩质量: " + inputPath);

        float minQuality = 0.1f;
        float maxQuality = 1.0f;
        float bestQuality = 0.8f;

        // 创建临时文件用于测试
        String tempPath = generateTempPath(outputPath);
        log.debug("创建临时文件用于测试: {}", tempPath);
        System.out.println("创建临时文件用于测试: " + tempPath);

        try {
            // 确保临时文件目录存在
            File tempFile = new File(tempPath);
            File parentDir = tempFile.getParentFile();
            if (!parentDir.exists()) {
                log.debug("创建临时文件目录: {}", parentDir.getAbsolutePath());
                System.out.println("创建临时文件目录: " + parentDir.getAbsolutePath());
                parentDir.mkdirs();
            }

            // 二分查找最优质量值
            for (int i = 0; i < 10; i++) { // 减少迭代次数，提高效率
                float testQuality = (minQuality + maxQuality) / 2;
                log.debug("第{}次测试，压缩质量: {}", i + 1, testQuality);
                System.out.println("第" + (i + 1) + "次测试，压缩质量: " + testQuality);

                try {
                    // 确保临时文件不存在
                    if (tempFile.exists()) {
                        tempFile.delete();
                    }

                    // 测试当前质量的文件大小
                    Thumbnails.of(inputPath)
                            .scale(1.0f)
                            .outputQuality(testQuality)
                            .toFile(tempPath);

                    // 等待文件写入完成
                    Thread.sleep(100);

                    long fileSize = tempFile.length();
                    log.debug("测试结果 - 文件大小: {} 字节, 目标大小: {} 字节", fileSize, TARGET_SIZE);
                    System.out.println("测试结果 - 文件大小: " + fileSize + " 字节, 目标大小: " + TARGET_SIZE + " 字节");

                    if (fileSize <= TARGET_SIZE) {
                        bestQuality = testQuality;
                        minQuality = testQuality;
                        log.debug("文件大小符合要求，调整最小质量为: {}", minQuality);
                        System.out.println("文件大小符合要求，调整最小质量为: " + minQuality);
                    } else {
                        maxQuality = testQuality;
                        log.debug("文件大小超过目标，调整最大质量为: {}", maxQuality);
                        System.out.println("文件大小超过目标，调整最大质量为: " + maxQuality);
                    }

                    // 如果已经很接近目标大小，提前退出
                    if (Math.abs(fileSize - TARGET_SIZE) < 10240) { // 10KB的容差
                        bestQuality = testQuality;
                        log.info("已接近目标大小，提前退出，最优质量: {}", bestQuality);
                        System.out.println("已接近目标大小，提前退出，最优质量: " + bestQuality);
                        break;
                    }

                    // 如果质量范围已经很小，提前退出
                    if (Math.abs(maxQuality - minQuality) < 0.05f) {
                        log.info("质量范围已经很小，提前退出，最优质量: {}", bestQuality);
                        System.out.println("质量范围已经很小，提前退出，最优质量: " + bestQuality);
                        break;
                    }

                } catch (IOException e) {
                    log.warn("测试压缩质量 {} 时出现异常: {}", testQuality, e.getMessage());
                    System.out.println("测试压缩质量 " + testQuality + " 时出现异常: " + e.getMessage());
                    // 如果当前质量测试失败，调整范围继续测试
                    maxQuality = testQuality;
                    continue;
                } catch (InterruptedException e) {
                    log.warn("等待文件写入时被中断");
                    System.out.println("等待文件写入时被中断");
                    Thread.currentThread().interrupt();
                    break;
                }

                // 删除临时文件
                if (tempFile.exists()) {
                    tempFile.delete();
                }
            }
        } catch (Exception e) {
            log.error("查找最优压缩质量时发生异常", e);
            System.err.println("查找最优压缩质量时发生异常: " + e.getMessage());
            e.printStackTrace();
            // 如果出错，使用默认质量
            bestQuality = 0.6f;
        } finally {
            // 清理可能残留的临时文件
            File tempFile = new File(tempPath);
            if (tempFile.exists()) {
                log.debug("清理残留临时文件: {}", tempPath);
                System.out.println("清理残留临时文件: " + tempPath);
                tempFile.delete();
            }
        }

        log.info("最优压缩质量查找完成: {}", bestQuality);
        System.out.println("最优压缩质量查找完成: " + bestQuality);
        return bestQuality;
    }

    /**
     * 生成临时文件路径，保持正确的图片扩展名
     *
     * @param outputPath 输出文件路径
     * @return 临时文件路径
     */
    private static String generateTempPath(String outputPath) {
        File outputFile = new File(outputPath);
        String fileName = outputFile.getName();
        String parentDir = outputFile.getParent();

        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1) {
            // 没有扩展名，默认添加.jpg
            return Paths.get(parentDir, fileName + "_comp.jpg").toString();
        } else {
            String nameWithoutExt = fileName.substring(0, dotIndex);
            String extension = fileName.substring(dotIndex);
            return Paths.get(parentDir, nameWithoutExt + "_comp" + extension).toString();
        }
    }

    /**
     * 生成压缩后的本地文件路径（在同一目录下）
     *
     * @param originalPath 原文件路径
     * @return 压缩后的文件路径
     */
    private static String generateCompressedLocalPath(String originalPath) {
        log.debug("生成压缩后本地文件路径: {}", originalPath);
        System.out.println("生成压缩后本地文件路径: " + originalPath);

        File originalFile = new File(originalPath);
        String parentDir = originalFile.getParent();
        String fileName = originalFile.getName();

        int dotIndex = fileName.lastIndexOf('.');
        String compressedPath;
        if (dotIndex == -1) {
            compressedPath = Paths.get(parentDir, fileName + "_comp.jpg").toString();
        } else {
            String nameWithoutExt = fileName.substring(0, dotIndex);
            String extension = fileName.substring(dotIndex);

            // 对于PNG文件，转换为JPG以获得更好的压缩效果
            if (".png".equalsIgnoreCase(extension)) {
                extension = ".jpg";
            }

            String compressedFileName = nameWithoutExt + "_comp" + extension;
            compressedPath = Paths.get(parentDir, compressedFileName).toString();
        }

        log.debug("生成的压缩文件路径: {}", compressedPath);
        System.out.println("生成的压缩文件路径: " + compressedPath);
        return compressedPath;
    }

    /**
     * 批量压缩图片（Web路径）
     *
     * @param webPaths Web访问路径数组
     * @return 压缩后的Web访问路径数组
     */
    public static String[] compressImagesByWebPath(String[] webPaths) {
        log.info("批量压缩图片，数量: {}", webPaths != null ? webPaths.length : 0);
        System.out.println("批量压缩图片，数量: " + (webPaths != null ? webPaths.length : 0));

        if (webPaths == null) {
            log.warn("Web路径数组为空");
            System.out.println("Web路径数组为空");
            return null;
        }

        String[] compressedPaths = new String[webPaths.length];
        for (int i = 0; i < webPaths.length; i++) {
            log.debug("批量压缩第{}张图片: {}", i + 1, webPaths[i]);
            System.out.println("批量压缩第" + (i + 1) + "张图片: " + webPaths[i]);
            compressedPaths[i] = compressImageByWebPath(webPaths[i]);
        }

        log.info("批量压缩完成");
        System.out.println("批量压缩完成");
        return compressedPaths;
    }

    /**
     * 检查路径是否为若依资源路径
     *
     * @param path 路径
     * @return 是否为资源路径
     */
    public static boolean isResourcePath(String path) {
        boolean result = StringUtils.isNotEmpty(path) && path.startsWith(Constants.RESOURCE_PREFIX + "/");
        log.debug("检查是否为资源路径: {} -> {}", path, result);
        System.out.println("检查是否为资源路径: " + path + " -> " + result);
        return result;
    }

    /**
     * 获取文件大小（字节）
     *
     * @param webPath Web访问路径
     * @return 文件大小，如果文件不存在返回-1
     */
    public static long getFileSize(String webPath) {
        log.debug("获取文件大小: {}", webPath);
        System.out.println("获取文件大小: " + webPath);

        String localPath = convertWebPathToLocalPath(webPath);
        if (StringUtils.isEmpty(localPath)) {
            log.warn("无法转换Web路径到本地路径");
            System.out.println("无法转换Web路径到本地路径");
            return -1;
        }

        File file = new File(localPath);
        long fileSize = file.exists() ? file.length() : -1;
        log.debug("文件大小: {} 字节", fileSize);
        System.out.println("文件大小: " + fileSize + " 字节");
        return fileSize;
    }


}
