package com.njzscloud.common.core.utils;

import com.njzscloud.common.core.ex.Exceptions;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

public class ImgUtil {

    // 最小缩放比例（防止无限缩小，可根据需求调整）
    private static final double MIN_SCALE = 0.1;
    // 最低质量压缩值（防止质量过低导致图片损坏）
    private static final float MIN_QUALITY = 0.1f;
    // 压缩衰减系数（每次递归压缩时，缩放/质量的衰减幅度）
    private static final double COMPRESS_ATTENUATION = 0.8;

    /**
     * 按质量压缩图片（返回字节流）
     */
    public static ByteArrayOutputStream compressByQuality(InputStream in, float quality, String format) throws IOException {
        BufferedImage bufferedImage = ImageIO.read(in);
        if (bufferedImage == null) {
            throw new IOException("读取图片流失败，图片格式不支持或流已关闭");
        }

        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
        if (!writers.hasNext()) {
            throw new IOException("不支持的图片格式：" + format);
        }
        ImageWriter writer = writers.next();

        ImageWriteParam param = writer.getDefaultWriteParam();
        if (param.canWriteCompressed()) {
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            // 确保质量不低于最小值
            param.setCompressionQuality(Math.max(quality, MIN_QUALITY));
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (ImageOutputStream ios = ImageIO.createImageOutputStream(out)) {
            writer.setOutput(ios);
            writer.write(null, new IIOImage(bufferedImage, null, null), param);
            return out;
        } finally {
            writer.dispose();
            // 关闭输入流，避免内存泄漏
            in.close();
        }
    }

    /**
     * 按尺寸缩放图片（返回字节流）
     */
    public static ByteArrayOutputStream compressByScale(InputStream in, double scale, String format) throws IOException {
        BufferedImage srcImage = ImageIO.read(in);
        if (srcImage == null) {
            throw new IOException("读取图片流失败，图片格式不支持或流已关闭");
        }

        // 确保缩放比例不低于最小值
        double actualScale = Math.max(scale, MIN_SCALE);
        int newWidth = Math.max(1, (int) (srcImage.getWidth() * actualScale));
        int newHeight = Math.max(1, (int) (srcImage.getHeight() * actualScale));

        BufferedImage destImage = new BufferedImage(newWidth, newHeight, srcImage.getType());
        Graphics2D g = destImage.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.drawImage(srcImage, 0, 0, newWidth, newHeight, null);
        g.dispose();

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(destImage, format, out);
        // 关闭输入流
        in.close();
        return out;
    }

    /**
     * 递归压缩图片：先校验大小，满足阈值直接返回；否则递归压缩直到满足阈值
     *
     * @param in            原始图片输入流
     * @param initialScale  初始压缩比例（尺寸+质量）
     * @param format        图片格式（jpg/png等）
     * @param sizeThreshold 体积阈值（例如：1024*100 表示100KB）
     * @return 压缩后的字节流
     */
    public static ByteArrayOutputStream compress(InputStream in, double initialScale, String format, long sizeThreshold) {
        try {
            // ========== 核心新增：第一步先校验原始图片大小 ==========
            byte[] originalBytes = readBytes(in);
            // 若原始体积已满足阈值，直接返回字节流，不执行任何压缩
            if (originalBytes.length <= sizeThreshold) {
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                out.write(originalBytes);
                return out;
            }

            // 原始体积不满足阈值，执行第一次组合压缩（质量+尺寸）
            ByteArrayOutputStream qualityOut = compressByQuality(
                    new ByteArrayInputStream(originalBytes),
                    (float) initialScale,
                    format
            );
            ByteArrayOutputStream scaleOut = compressByScale(
                    new ByteArrayInputStream(qualityOut.toByteArray()),
                    initialScale,
                    format
            );

            // 校验压缩后的体积是否满足阈值
            long currentSize = scaleOut.size();
            if (currentSize <= sizeThreshold) {
                return scaleOut; // 满足阈值，直接返回
            }

            // 计算下一次压缩的比例（衰减）
            double nextScale = initialScale * COMPRESS_ATTENUATION;

            // 边界校验：防止无限递归
            if (nextScale < MIN_SCALE) {
                // 已达到最小压缩比例，返回当前结果（避免图片失效）
                return scaleOut;
            }

            // 递归调用：使用衰减后的比例继续压缩
            return compress(
                    new ByteArrayInputStream(scaleOut.toByteArray()),
                    nextScale,
                    format,
                    sizeThreshold
            );

        } catch (IOException e) {
            throw Exceptions.error(e, "图片压缩失败");
        }
    }

    /**
     * 辅助方法：读取输入流的所有字节（避免流复用导致的问题）
     */
    private static byte[] readBytes(InputStream in) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        byte[] temp = new byte[1024];
        int len;
        while ((len = in.read(temp)) != -1) {
            buffer.write(temp, 0, len);
        }
        in.close();
        return buffer.toByteArray();
    }

}
