package com.test.bijihoudaun.util;

import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 图形验证码工具类
 * 使用本地内存存储验证码，带容量限制
 */
@Slf4j
public class CaptchaUtil {

    // 验证码有效期（分钟）
    private static final int CAPTCHA_EXPIRE_MINUTES = 5;
    // 验证码长度
    private static final int CAPTCHA_LENGTH = 4;
    // 图片宽度
    private static final int IMAGE_WIDTH = 120;
    // 图片高度
    private static final int IMAGE_HEIGHT = 40;
    // 最大存储验证码数（防止内存溢出）
    private static final int MAX_CAPTCHAS = 5000;

    // 存储验证码：key=验证码ID，value=验证码记录
    private static final ConcurrentHashMap<String, CaptchaRecord> captchaStore = new ConcurrentHashMap<>();

    // 使用 ThreadLocal 确保线程安全
    private static final ThreadLocal<SecureRandom> random = ThreadLocal.withInitial(SecureRandom::new);

    // 定期清理线程池
    private static final ScheduledExecutorService cleanupScheduler = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread t = new Thread(r, "captcha-cleanup");
        t.setDaemon(true);
        return t;
    });

    static {
        // 每5分钟清理一次过期验证码
        cleanupScheduler.scheduleAtFixedRate(CaptchaUtil::cleanupExpiredCaptchas,
                5, 5, TimeUnit.MINUTES);
    }

    private static class CaptchaRecord {
        String code;
        LocalDateTime createTime;

        CaptchaRecord(String code) {
            this.code = code;
            this.createTime = LocalDateTime.now();
        }

        boolean isExpired() {
            return ChronoUnit.MINUTES.between(createTime, LocalDateTime.now()) > CAPTCHA_EXPIRE_MINUTES;
        }
    }

    /**
     * 验证码结果
     */
    public static class CaptchaResult {
        private String captchaId;
        private String base64Image;

        public CaptchaResult(String captchaId, String base64Image) {
            this.captchaId = captchaId;
            this.base64Image = base64Image;
        }

        public String getCaptchaId() {
            return captchaId;
        }

        public String getBase64Image() {
            return base64Image;
        }
    }

    /**
     * 生成验证码
     * @return 包含验证码ID和Base64图片的结果
     */
    public static CaptchaResult generateCaptcha() {
        // 检查内存状态
        if (MemoryProtector.isMemoryInsufficient()) {
            log.error("内存不足，拒绝生成验证码");
            throw new RuntimeException("服务器繁忙，请稍后再试");
        }

        // 生成验证码ID
        String captchaId = UuidV7.uuid();

        // 生成验证码字符
        String code = generateCode();

        // 生成图片
        String base64Image = generateImage(code);

        // 存储验证码
        captchaStore.put(captchaId, new CaptchaRecord(code));

        return new CaptchaResult(captchaId, base64Image);
    }

    /**
     * 验证验证码
     * @param captchaId 验证码ID
     * @param code 用户输入的验证码
     * @return 验证成功返回true，失败返回false
     */
    public static boolean validateCaptcha(String captchaId, String code) {
        if (captchaId == null || code == null) {
            return false;
        }

        CaptchaRecord record = captchaStore.get(captchaId);
        if (record == null || record.isExpired()) {
            // 验证码不存在或已过期，移除
            if (record != null) {
                captchaStore.remove(captchaId);
            }
            return false;
        }

        // 验证码比对（不区分大小写）
        boolean success = record.code.equalsIgnoreCase(code);

        // 验证成功后立即删除（一次性使用）
        if (success) {
            captchaStore.remove(captchaId);
        }

        return success;
    }

    /**
     * 生成随机验证码
     */
    private static String generateCode() {
        String chars = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
        StringBuilder sb = new StringBuilder();
        SecureRandom r = random.get();
        for (int i = 0; i < CAPTCHA_LENGTH; i++) {
            sb.append(chars.charAt(r.nextInt(chars.length())));
        }
        return sb.toString();
    }

    /**
     * 生成验证码图片（Base64格式）
     */
    private static String generateImage(String code) {
        BufferedImage image = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = image.createGraphics();

        try {
            // 设置背景色
            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);

            // 绘制干扰线
            g2d.setColor(Color.LIGHT_GRAY);
            SecureRandom r = random.get();
            for (int i = 0; i < 5; i++) {
                int x1 = r.nextInt(IMAGE_WIDTH);
                int y1 = r.nextInt(IMAGE_HEIGHT);
                int x2 = r.nextInt(IMAGE_WIDTH);
                int y2 = r.nextInt(IMAGE_HEIGHT);
                g2d.drawLine(x1, y1, x2, y2);
            }

            // 绘制验证码字符
            g2d.setFont(new Font("Arial", Font.BOLD, 24));
            int x = 15;
            for (int i = 0; i < code.length(); i++) {
                // 随机颜色
                g2d.setColor(new Color(
                        r.nextInt(100),
                        r.nextInt(100),
                        r.nextInt(100)
                ));
                // 随机旋转角度
                int angle = r.nextInt(30) - 15;
                g2d.rotate(Math.toRadians(angle), x + 10, 25);
                g2d.drawString(String.valueOf(code.charAt(i)), x, 28);
                g2d.rotate(-Math.toRadians(angle), x + 10, 25);
                x += 25;
            }

            // 添加噪点
            for (int i = 0; i < 30; i++) {
                int x1 = r.nextInt(IMAGE_WIDTH);
                int y1 = r.nextInt(IMAGE_HEIGHT);
                image.setRGB(x1, y1, Color.GRAY.getRGB());
            }

            // 转为 Base64
            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                ImageIO.write(image, "png", baos);
                byte[] imageBytes = baos.toByteArray();
                return "data:image/png;base64," + java.util.Base64.getEncoder().encodeToString(imageBytes);
            }
        } catch (IOException e) {
            throw new RuntimeException("生成验证码图片失败", e);
        } finally {
            g2d.dispose();
        }
    }

    /**
     * 清理过期验证码
     */
    private static void cleanupExpiredCaptchas() {
        try {
            int removed = 0;
            for (Map.Entry<String, CaptchaRecord> entry : captchaStore.entrySet()) {
                if (entry.getValue().isExpired()) {
                    captchaStore.remove(entry.getKey());
                    removed++;
                }
            }
            if (removed > 0) {
                log.debug("清理了 {} 个过期验证码", removed);
            }
        } catch (Exception e) {
            log.error("清理过期验证码失败", e);
        }
    }

    /**
     * 获取当前验证码数量（用于监控）
     */
    public static int getCaptchaCount() {
        return captchaStore.size();
    }
}
