package com.gic.haoban.manage.service.context.combined;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.gic.commons.util.TraceIdUtil;
import com.gic.haoban.manage.api.qdto.combined.CombinedQDTO;
import com.gic.haoban.manage.service.util.ApolloUtils;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created by wangzubo on 2024/10/27.
 */
public class ImageCombined {

    private static final String APPLICATION = "application";

    private static final Logger logger = LoggerFactory.getLogger(ImageCombined.class);

    private static final Cache<String, BufferedImage> imageCache = Caffeine.newBuilder()
            .maximumWeight(200 * 1024 * 1024) // 100MB
            .weigher((String key, BufferedImage image) -> image.getWidth() * image.getHeight() * 3)
            .removalListener((key, value, cause) -> {
                logger.info("移除缓存:{}",key);
                if (value != null) {
                    value.flush();
                }
            })
            .expireAfterAccess(10, TimeUnit.MINUTES) // 可设置失效时间
            .build();

    public static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
            ApolloUtils.combinedCorePoolSize(),
            ApolloUtils.combinedMaximumPoolSize(),
            60L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(40),
            new ThreadPoolExecutor.AbortPolicy()
    );

    private static void refreshThreadPool(ConfigChangeEvent changeEvent) {
        if (!StringUtils.equals(changeEvent.getNamespace(), APPLICATION)) {
            return;
        }
        TraceIdUtil.traceBegin(TraceIdUtil.EntranceType.QUARTZ);
        ConfigChange combinedCorePoolSizeChange = changeEvent.getChange("combinedCorePoolSize");
        ConfigChange combinedMaximumPoolSizeChange = changeEvent.getChange("combinedMaximumPoolSize");
        if ((combinedCorePoolSizeChange != null && combinedCorePoolSizeChange.getNewValue() != null)
                || (combinedMaximumPoolSizeChange != null && combinedMaximumPoolSizeChange.getNewValue() != null)) {
            Integer corePoolSize = ApolloUtils.combinedCorePoolSize();
            Integer maximumPoolSize = ApolloUtils.combinedMaximumPoolSize();
            logger.info("动态调整线程池corePoolSize:{},maximumPoolSize:{}", corePoolSize, maximumPoolSize);
            EXECUTOR.setCorePoolSize(corePoolSize);
            EXECUTOR.setMaximumPoolSize(maximumPoolSize);
        }
        TraceIdUtil.traceEnd();
    }

    public static final String IMAGE_REDIS_KEY = "haoban-manage3-service:combined_image:";

    private static final int MAX_HEIGHT_NO_WHITE_SPACE = 1500;
    private static final int MAX_HEIGHT_WITH_WHITE_SPACE = 1340;
    private static final int MIN_WIDTH = 500;
    private static final int WHITE_SPACE_HEIGHT = 160;
    private static final int QR_WIDTH_REFERENCE = 136;


    private static final String LARGE_IMAGE_URL = "https://revan-1251519181.cos.ap-shanghai.myqcloud.com/image/material_content-29dd73e4f9fb4a9eaf38ae1a3158bc91.jpg?imageView2/2/w/1080/h/10800/format/jpg";
    private static final String QR_CODE_URL = "https://gicinner-1251519181.cos.ap-shanghai.myqcloud.com/image/material_content-4ffc77073ca1476fb264bf1be9f11383.png";
    private static final String STORE_NAME = "杭州大厦";
    private static final String GUIDE_NAME = "翁坚鹏为您推荐";
    private static final String OUTPUT_PATH = "/Users/wang/Downloads/output_image6.jpg";

    static {
        ImageIO.scanForPlugins();  // 初始化图像插件
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(ImageCombined::refreshThreadPool);
    }

    public static void main(String[] args) throws Exception{
        BufferedImage largeImage = loadImageWithCache(LARGE_IMAGE_URL);
        BufferedImage qrCodeImage = ImageIO.read(new URL(QR_CODE_URL));

        int showStyle = 0;

        BufferedImage combinedImage = generateImage(largeImage, qrCodeImage, showStyle,STORE_NAME, GUIDE_NAME);
        ImageIO.write(combinedImage, "jpg", new File(OUTPUT_PATH));
    }

    public static byte[] getCombinedImage(CombinedQDTO combinedQDTO) throws Exception{
        String imageUrl = combinedQDTO.getImageUrl();
        String qrCodeUrl = combinedQDTO.getQrCodeUrl();
        Integer showStyle = combinedQDTO.getShowStyle();
        String lineOne = combinedQDTO.getLineOne();
        String lineTwo = combinedQDTO.getLineTwo();
        if(imageUrl.contains("?")) {
            logger.info("url有参数={}",imageUrl);
            imageUrl = imageUrl.split("\\?")[0] ;
        }
        //压缩图片的参数
        imageUrl = imageUrl + "?imageView2/2/w/1080/h/10800/format/jpg";
        logger.info("加载要合成的图片开始1");
        BufferedImage largeImage = loadImageWithCache(imageUrl);
        logger.info("加载要合成的图片开始2");
        BufferedImage qrCodeImage = loadImageWithReader(qrCodeUrl);
        logger.info("加载要合成的图片结束");
        if (largeImage == null || qrCodeImage == null) {
            return null;
        }
        BufferedImage combinedImage = generateImage(largeImage, qrCodeImage, showStyle, lineOne, lineTwo);
        if (combinedImage == null) {
            return null;
        }

//        ImageIO.write(combinedImage, "jpg", new File(OUTPUT_PATH));
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            ImageIO.write(combinedImage, "jpg", baos);
            // 返回字节数组
            return baos.toByteArray();
        }finally {
            qrCodeImage.flush();
            combinedImage.flush();
        }
    }

    private static BufferedImage loadImageWithReader(String url) {
        try {
            URL imageUrl = new URL(url);
            try (InputStream inputStream = imageUrl.openStream();
                 ImageInputStream imageInputStream = ImageIO.createImageInputStream(inputStream)) {

                Iterator<ImageReader> readers = ImageIO.getImageReaders(imageInputStream);
                if (readers.hasNext()) {
                    ImageReader reader = readers.next();
                    reader.setInput(imageInputStream, true);
                    return reader.read(0);
                }
            }
        } catch (IOException e) {
            logger.error("读取异常: {}", url, e);
        }
        return null;
    }


    /**
     * 从缓存中加载图像
     */
    private static BufferedImage loadImageWithCache(String url) {
        boolean useCache = ApolloUtils.combinedCache();
        if (!useCache) {
            logger.info("非缓存加载图片:{}", url);
            return loadImageWithReader(url);
        }
        return imageCache.get(url, key -> {
            try {
                logger.info("加载图片buffered:{}", url);
                return loadImageWithReader(key);  // 使用读取器方法
            } catch (Exception e) {
                logger.error("获取图片异常", e);
                return null;
            }
        });
    }

    private static BufferedImage generateImage(BufferedImage image, BufferedImage qrCode, int showStyle,String lineOne,String lineTwo){

        logger.info("合成图片开始");
        int width = image.getWidth();
        int height = image.getHeight();
        int qrWidth = QR_WIDTH_REFERENCE;
        if (showStyle == 1) {
            if (height > MAX_HEIGHT_NO_WHITE_SPACE) {
                width = width * MAX_HEIGHT_NO_WHITE_SPACE / height;
                height = MAX_HEIGHT_NO_WHITE_SPACE;
            }
        } else {
            if (height > MAX_HEIGHT_WITH_WHITE_SPACE) {
                width = width * MAX_HEIGHT_WITH_WHITE_SPACE / height;
                height = MAX_HEIGHT_WITH_WHITE_SPACE;
            }
            width = Math.max(width, MIN_WIDTH);
        }
        int finalHeight = height + (showStyle == 1 ? 0 : WHITE_SPACE_HEIGHT);
        BufferedImage resizedQRCode = null;
        BufferedImage resizedImage = null;

        try {
            BufferedImage combinedImage = new BufferedImage(width, finalHeight, BufferedImage.TYPE_INT_RGB);
            if (showStyle == 1) {
                qrWidth = width * QR_WIDTH_REFERENCE / 750;
            }
            resizedQRCode = Thumbnails.of(qrCode).size(qrWidth, qrWidth).asBufferedImage();
            resizedImage = Thumbnails.of(image).size(width, height).asBufferedImage();

            Graphics2D g = combinedImage.createGraphics();
            g.setColor(Color.WHITE);
            g.drawImage(resizedImage, 0, 1, null);
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            if (showStyle == 1) {
                // 不需要留白时，QR码距离底部20px
                g.drawImage(resizedQRCode, width - qrWidth - 20, finalHeight - qrWidth - 20, null);
            } else {
                // 需要留白时
                addWhiteSpace(g, width, finalHeight, resizedQRCode, qrWidth, lineOne, lineTwo);
            }

            g.dispose();
            logger.info("合成图片结束");
            return combinedImage;
        } catch (Exception e) {
            logger.error("生成合成图像时发生异常:", e);
            return null;
        } finally {
            if (resizedQRCode != null) {
                resizedQRCode.flush();
            }
            if (resizedImage != null) {
                resizedImage.flush();
            }
        }
    }

    private static void addWhiteSpace(Graphics2D g, int width, int finalHeight, BufferedImage qrCode, int qrWidth,String lineOne,String lineTwo) {
        // 添加白色背景
        g.setColor(Color.WHITE);
        g.fillRect(0, finalHeight - WHITE_SPACE_HEIGHT, width, WHITE_SPACE_HEIGHT);
        String font = ApolloUtils.combinedFonts();
        int type = ApolloUtils.combinedFontsType();
        // 添加门店名称，距底部88px
        g.setColor(new Color(36, 40, 53));
        g.setFont(new Font(font, type, 30));
        logger.info("使用字体:{}",g.getFont().getFontName());
        drawSingleLineText(g, lineOne, 30, finalHeight - WHITE_SPACE_HEIGHT + 72, width - 196); // 留出左右边距

        // 添加推荐人信息，距底部40px
        g.setColor(new Color(151, 155, 165));
        g.setFont(new Font(font, type, 26));
        drawSingleLineText(g, lineTwo, 30, finalHeight - WHITE_SPACE_HEIGHT + 120, width - 196); // 留出左右边距

        // 添加二维码，距底部12px
        int qrBottomPadding = 12;
        g.drawImage(qrCode, width - qrWidth - qrBottomPadding, finalHeight - qrWidth - qrBottomPadding, null);
    }

    private static void drawSingleLineText(Graphics2D g, String text, int x, int y, int maxWidth) {
        FontMetrics metrics = g.getFontMetrics(g.getFont());
        String truncatedText = text;

        // 计算文本宽度并进行截断
        if (metrics.stringWidth(text) > maxWidth) {
            while (metrics.stringWidth(truncatedText + "...") > maxWidth && !truncatedText.isEmpty()) {
                truncatedText = truncatedText.substring(0, truncatedText.length() - 2);
            }
            truncatedText += "..."; // 添加省略号
        }

        g.drawString(truncatedText, x, y);
    }

}
