package com.jeesharp.business.modules.tool.service.impl;

import com.jeesharp.business.modules.tool.dto.ImageProcessDTO;
import com.jeesharp.business.modules.tool.service.ImageToolService;
import com.jeesharp.business.modules.tool.vo.ImageAnalysisVO;
import com.jeesharp.business.modules.tool.vo.ImageProcessResultVO;
import com.jeesharp.framework.file.options.UploadOptions;
import com.jeesharp.framework.file.result.UploadResult;
import com.jeesharp.framework.file.service.FileStorageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

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.color.ColorSpace;
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;

/**
 * 图片工具服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ImageToolServiceImpl implements ImageToolService {

    private final FileStorageService fileStorageService;

    private static final String DEFAULT_BUCKET = "images/tool";
    private static final int MIN_QUALITY = 5;
    private static final int QUALITY_STEP = 5;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ImageProcessResultVO processImage(MultipartFile file, ImageProcessDTO dto) {
        // 1. 处理图片
        byte[] processedBytes = processImageToBytes(file, dto);

        // 2. 生成文件名
        String format = dto.getFormat();
        String extension = "jpeg".equals(format) ? "jpg" : format;
        String originalName = file.getOriginalFilename();
        String baseName = originalName != null ? originalName.substring(0, originalName.lastIndexOf('.')) : "processed-image";
        String fileName = baseName + "_processed." + extension;

        // 3. 上传到文件存储
        String bucket = dto.getBucket() != null ? dto.getBucket() : DEFAULT_BUCKET;
        UploadOptions options = UploadOptions.builder()
                .objectType("image_tool")
                .build();

        try (InputStream inputStream = new ByteArrayInputStream(processedBytes)) {
            UploadResult uploadResult = fileStorageService.uploadWithResult(inputStream, fileName, bucket, options);

            // 4. 构建返回结果
            return ImageProcessResultVO.builder()
                    .url(uploadResult.getUrl())
                    .fileKey(uploadResult.getFileKey())
                    .fileName(uploadResult.getFileName())
                    .fileSize(uploadResult.getSize())
                    .width(dto.getWidth())
                    .height(dto.getHeight())
                    .format(format)
                    .build();
        } catch (IOException e) {
            log.error("图片上传失败", e);
            throw new RuntimeException("图片上传失败: " + e.getMessage());
        }
    }

    @Override
    public byte[] processImageToBytes(MultipartFile file, ImageProcessDTO dto) {
        try {
            // 1. 读取原始图片
            BufferedImage originalImage = ImageIO.read(file.getInputStream());
            if (originalImage == null) {
                throw new IllegalArgumentException("无法读取图片文件");
            }

            // 2. 缩放图片
            int targetWidth = dto.getWidth();
            int targetHeight = dto.getHeight();
            String format = dto.getFormat();

            BufferedImage resizedImage = resizeImage(originalImage, targetWidth, targetHeight, format);

            // 3. 根据格式和质量导出
            byte[] result = exportImage(resizedImage, format, dto.getQuality());

            // 4. 检查文件大小限制
            Integer maxFileSizeKB = dto.getMaxFileSizeKB();
            if (maxFileSizeKB != null && maxFileSizeKB > 0) {
                long maxSizeBytes = maxFileSizeKB * 1024L;
                if (result.length > maxSizeBytes) {
                    result = adjustQualityToFitSize(resizedImage, format, dto.getQuality(), maxSizeBytes);
                }
            }

            return result;
        } catch (IOException e) {
            log.error("图片处理失败", e);
            throw new RuntimeException("图片处理失败: " + e.getMessage());
        }
    }

    /**
     * 缩放图片
     */
    private BufferedImage resizeImage(BufferedImage original, int targetWidth, int targetHeight, String format) {
        // 如果是 jpeg 格式，需要处理透明通道
        if ("jpeg".equals(format) && original.getType() != BufferedImage.TYPE_INT_RGB) {
            BufferedImage rgbImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = rgbImage.createGraphics();
            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, targetWidth, targetHeight);
            g2d.dispose();

            // 使用 Thumbnailator 缩放（默认使用高质量 Lanczos3 算法）
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                Thumbnails.of(original)
                        .size(targetWidth, targetHeight)
                        .keepAspectRatio(false)
                        .outputFormat("png")
                        .outputQuality(1.0)
                        .toOutputStream(baos);

                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                BufferedImage tempImage = ImageIO.read(bais);

                g2d = rgbImage.createGraphics();
                g2d.drawImage(tempImage, 0, 0, targetWidth, targetHeight, null);
                g2d.dispose();

                return rgbImage;
            } catch (IOException e) {
                log.warn("Thumbnailator 处理失败，使用默认缩放", e);
                return createResizedImage(original, targetWidth, targetHeight);
            }
        }

        // 使用 Thumbnailator 缩放（默认使用高质量 Lanczos3 算法）
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            Thumbnails.of(original)
                    .size(targetWidth, targetHeight)
                    .keepAspectRatio(false)
                    .outputFormat("png")
                    .outputQuality(1.0)
                    .toOutputStream(baos);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            return ImageIO.read(bais);
        } catch (IOException e) {
            log.warn("Thumbnailator 处理失败，使用默认缩放", e);
            return createResizedImage(original, targetWidth, targetHeight);
        }
    }

    /**
     * 使用 Graphics2D 创建缩放后的图片
     */
    private BufferedImage createResizedImage(BufferedImage original, int targetWidth, int targetHeight) {
        BufferedImage resized = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = resized.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.drawImage(original, 0, 0, targetWidth, targetHeight, null);
        g2d.dispose();
        return resized;
    }

    /**
     * 导出图片为指定格式和质量
     */
    private byte[] exportImage(BufferedImage image, String format, int quality) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        if ("png".equals(format)) {
            // PNG 是无损格式，quality 参数无效
            ImageIO.write(image, "png", baos);
        } else if ("gif".equals(format)) {
            // GIF 格式
            ImageIO.write(image, "gif", baos);
        } else {
            // JPEG 和 WebP 使用质量控制
            String imageioFormat = "jpeg".equals(format) ? "jpg" : format;

            Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(imageioFormat);
            if (writers.hasNext()) {
                ImageWriter writer = writers.next();
                ImageWriteParam param = writer.getDefaultWriteParam();
                param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                param.setCompressionQuality(quality / 100.0f);

                try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
                    writer.setOutput(ios);
                    writer.write(null, new IIOImage(image, null, null), param);
                }
                writer.dispose();
            } else {
                // 降级处理
                ImageIO.write(image, imageioFormat, baos);
            }
        }

        return baos.toByteArray();
    }

    /**
     * 自动调整质量以满足文件大小限制（使用二分法精确查找最佳质量）
     */
    private byte[] adjustQualityToFitSize(BufferedImage image, String format, int initialQuality, long maxSizeBytes) {
        int low = MIN_QUALITY;
        int high = initialQuality;
        byte[] bestResult = null;

        // 二分法查找满足大小限制的最高质量
        while (low <= high) {
            int mid = (low + high) / 2;
            try {
                byte[] result = exportImage(image, format, mid);
                if (result.length <= maxSizeBytes) {
                    bestResult = result;
                    low = mid + 1;  // 尝试更高质量
                } else {
                    high = mid - 1;  // 降低质量
                }
            } catch (IOException e) {
                log.warn("图片导出失败，质量: {}", mid, e);
                high = mid - 1;
            }
        }

        if (bestResult != null) {
            return bestResult;
        }

        // 降级处理：尝试降低分辨率
        return reduceResolutionToFit(image, format, initialQuality, maxSizeBytes);
    }

    /**
     * 降低分辨率以满足文件大小限制
     */
    private byte[] reduceResolutionToFit(BufferedImage image, String format, int quality, long maxSizeBytes) {
        double scale = 0.9;
        while (scale >= 0.5) {
            int newWidth = (int)(image.getWidth() * scale);
            int newHeight = (int)(image.getHeight() * scale);
            BufferedImage resized = resizeImage(image, newWidth, newHeight, format);
            try {
                byte[] result = exportImage(resized, format, quality);
                if (result.length <= maxSizeBytes) {
                    return result;
                }
            } catch (IOException e) {
                log.warn("图片导出失败，缩放比例: {}", scale, e);
            }
            scale -= 0.1;
        }

        // 最后尝试最低质量
        try {
            return exportImage(image, format, MIN_QUALITY);
        } catch (IOException e) {
            throw new RuntimeException("无法将图片压缩到指定大小");
        }
    }

    @Override
    public ImageAnalysisVO analyzeImage(MultipartFile file) {
        try {
            BufferedImage image = ImageIO.read(file.getInputStream());
            if (image == null) {
                throw new IllegalArgumentException("无法读取图片文件");
            }

            int width = image.getWidth();
            int height = image.getHeight();
            long fileSize = file.getSize();
            String format = file.getContentType();
            boolean hasAlpha = image.getColorModel().hasAlpha();
            String colorSpace = image.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_RGB ? "RGB" : "其他";

            // 智能推荐参数
            String recommendedFormat = recommendFormat(hasAlpha, fileSize);
            int recommendedQuality = recommendQuality(fileSize);
            int[] recommendedSize = recommendSize(width, height);
            int recommendedMaxFileSizeKB = recommendMaxFileSizeKB(recommendedSize[0], recommendedSize[1], recommendedFormat, recommendedQuality);

            // 生成推荐原因
            StringBuilder reason = new StringBuilder();
            reason.append(String.format("原图 %d×%dpx (%s)", width, height, formatFileSize(fileSize)));
            if (hasAlpha) {
                reason.append("，包含透明通道");
            }
            reason.append(String.format("，推荐使用 %s 格式", recommendedFormat.toUpperCase()));
            reason.append(String.format("，质量 %d%%", recommendedQuality));

            return ImageAnalysisVO.builder()
                    .width(width)
                    .height(height)
                    .fileSize(fileSize)
                    .format(format)
                    .hasAlpha(hasAlpha)
                    .colorSpace(colorSpace)
                    .recommendedFormat(recommendedFormat)
                    .recommendedQuality(recommendedQuality)
                    .recommendedWidth(recommendedSize[0])
                    .recommendedHeight(recommendedSize[1])
                    .recommendedMaxFileSizeKB(recommendedMaxFileSizeKB)
                    .recommendationReason(reason.toString())
                    .build();
        } catch (IOException e) {
            log.error("图片分析失败", e);
            throw new RuntimeException("图片分析失败: " + e.getMessage());
        }
    }

    /**
     * 推荐输出格式
     */
    private String recommendFormat(boolean hasAlpha, long fileSize) {
        // 如果有透明通道，推荐 PNG 或 WebP
        if (hasAlpha) {
            return fileSize > 1024 * 1024 ? "webp" : "png";
        }
        // 照片类图片推荐 WebP（更好的压缩率）
        return "webp";
    }

    /**
     * 推荐质量
     */
    private int recommendQuality(long fileSize) {
        if (fileSize > 5 * 1024 * 1024) return 70;
        if (fileSize > 2 * 1024 * 1024) return 75;
        if (fileSize > 1 * 1024 * 1024) return 80;
        return 85;
    }

    /**
     * 推荐尺寸（保持比例，不超过 1920px）
     */
    private int[] recommendSize(int width, int height) {
        double aspectRatio = (double) width / height;
        int maxWidth = 1920;

        if (width <= maxWidth) {
            return new int[]{width, height};
        }

        int recommendedWidth = maxWidth;
        int recommendedHeight = (int) (recommendedWidth / aspectRatio);
        return new int[]{recommendedWidth, recommendedHeight};
    }

    /**
     * 推荐文件大小限制
     */
    private int recommendMaxFileSizeKB(int width, int height, String format, int quality) {
        if ("png".equals(format)) {
            // PNG 无损格式，大小限制较宽松
            return Math.max(width * height / 1000, 500);
        }
        // 有损格式，根据质量设置限制
        int estimatedKB = (int) (width * height * 0.1 / 1024);
        return Math.max(100, Math.min(estimatedKB, 1000));
    }

    /**
     * 格式化文件大小
     */
    private String formatFileSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024));
        return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
    }
}
