package com.njcn.gather.device.util;

import lombok.AllArgsConstructor;
import lombok.Data;
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.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.Iterator;
import java.util.Locale;

public final class PqDevImageSupport {

    private static final long MAX_FILE_SIZE = 10L * 1024L * 1024L;
    private static final int MAX_EDGE = 1600;
    private static final float JPEG_QUALITY = 0.82f;

    private PqDevImageSupport() {
    }

    public static ImageEchoPayload buildEchoPayload(byte[] imageBytes) {
        if (imageBytes == null || imageBytes.length == 0) {
            return new ImageEchoPayload(false, null, null);
        }
        return new ImageEchoPayload(true, Base64.getEncoder().encodeToString(imageBytes), detectContentType(imageBytes));
    }

    public static String detectContentType(byte[] imageBytes) {
        if (imageBytes == null || imageBytes.length < 4) {
            throw new IllegalArgumentException("图片内容无效");
        }
        if (isPng(imageBytes)) {
            return "image/png";
        }
        if (isJpeg(imageBytes)) {
            return "image/jpeg";
        }
        throw new IllegalArgumentException("仅支持 JPG、PNG 图片");
    }

    public static byte[] processUpload(MultipartFile image) throws IOException {
        if (image == null || image.isEmpty()) {
            return null;
        }
        if (image.getSize() > MAX_FILE_SIZE) {
            throw new IllegalArgumentException("图片大小不能超过 10MB");
        }

        String normalizedName = normalize(image.getOriginalFilename());
        String normalizedContentType = normalize(image.getContentType());
        boolean allowedType = normalizedContentType.equals("image/jpeg")
                || normalizedContentType.equals("image/png")
                || normalizedName.endsWith(".jpg")
                || normalizedName.endsWith(".jpeg")
                || normalizedName.endsWith(".png");
        if (!allowedType) {
            throw new IllegalArgumentException("仅支持 JPG、PNG 图片");
        }

        byte[] originalBytes = image.getBytes();
        String contentType = detectContentType(originalBytes);
        BufferedImage source;
        try (InputStream inputStream = image.getInputStream()) {
            source = ImageIO.read(inputStream);
        }
        if (source == null) {
            throw new IllegalArgumentException("图片内容无效");
        }

        BufferedImage target = scaleIfNeeded(source);
        if ("image/png".equals(contentType)) {
            return writePng(target);
        }
        return writeJpeg(target);
    }

    public static String getExtension(String contentType) {
        return "image/png".equals(contentType) ? "png" : "jpg";
    }

    private static BufferedImage scaleIfNeeded(BufferedImage source) {
        int width = source.getWidth();
        int height = source.getHeight();
        int max = Math.max(width, height);
        if (max <= MAX_EDGE) {
            return source;
        }

        double scale = (double) MAX_EDGE / max;
        int targetWidth = Math.max(1, (int) Math.round(width * scale));
        int targetHeight = Math.max(1, (int) Math.round(height * scale));
        int type = source.getType() == BufferedImage.TYPE_CUSTOM ? BufferedImage.TYPE_INT_ARGB : source.getType();
        BufferedImage target = new BufferedImage(targetWidth, targetHeight, type);
        Graphics2D graphics = target.createGraphics();
        try {
            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            graphics.drawImage(source, 0, 0, targetWidth, targetHeight, null);
        } finally {
            graphics.dispose();
        }
        return target;
    }

    private static byte[] writePng(BufferedImage image) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "png", outputStream);
        return outputStream.toByteArray();
    }

    private static byte[] writeJpeg(BufferedImage image) throws IOException {
        BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = rgbImage.createGraphics();
        try {
            graphics.drawImage(image, 0, 0, null);
        } finally {
            graphics.dispose();
        }

        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
        if (!writers.hasNext()) {
            throw new IOException("未找到 JPG 图片编码器");
        }
        ImageWriter writer = writers.next();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(outputStream)) {
            writer.setOutput(imageOutputStream);
            ImageWriteParam writeParam = writer.getDefaultWriteParam();
            if (writeParam.canWriteCompressed()) {
                writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                writeParam.setCompressionQuality(JPEG_QUALITY);
            }
            writer.write(null, new IIOImage(rgbImage, null, null), writeParam);
        } finally {
            writer.dispose();
        }
        return outputStream.toByteArray();
    }

    private static boolean isPng(byte[] imageBytes) {
        return imageBytes.length >= 8
                && (imageBytes[0] & 0xFF) == 0x89
                && imageBytes[1] == 0x50
                && imageBytes[2] == 0x4E
                && imageBytes[3] == 0x47
                && imageBytes[4] == 0x0D
                && imageBytes[5] == 0x0A
                && imageBytes[6] == 0x1A
                && imageBytes[7] == 0x0A;
    }

    private static boolean isJpeg(byte[] imageBytes) {
        return imageBytes.length >= 3
                && (imageBytes[0] & 0xFF) == 0xFF
                && (imageBytes[1] & 0xFF) == 0xD8
                && (imageBytes[2] & 0xFF) == 0xFF;
    }

    private static String normalize(String value) {
        return value == null ? "" : value.toLowerCase(Locale.ROOT);
    }

    @Data
    @AllArgsConstructor
    public static class ImageEchoPayload {
        private boolean hasImage;
        private String imageBase64;
        private String imageContentType;
    }
}
