package com.ruoyi.common.utils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * 许可证加密工具类
 * 使用AES加密算法生成和验证许可证密钥
 *
 * @author ruoyi
 * @date 2026-02-09
 */
public class LicenseEncryptUtil {
    // AES加密密钥（生产环境应该使用更复杂的密钥，并妥善保管）
    private static final String SECRET_KEY = "RuoYi@2026#License$Key!Secure*9527";

    // 加密算法
    private static final String ALGORITHM = "AES";

    // 字段分隔符
    private static final String SEPARATOR = "|";

    /**
     * 生成许可证密钥
     *
     * @param activationTime 激活时间（时间戳）
     * @param expiryTime     到期时间（时间戳）
     * @param trialDays      试用天数
     * @return 加密后的许可证密钥
     */
    public static String generateLicenseKey(long activationTime, long expiryTime, int trialDays) {
        try {
            // 构建原始数据：激活时间|到期时间|机器码|试用天数|校验码
            String rawData = activationTime + SEPARATOR +
                    expiryTime + SEPARATOR +
                    trialDays + SEPARATOR +
                    generateChecksum(activationTime, expiryTime, trialDays);

            // AES加密
            byte[] encrypted = encrypt(rawData);

            // Base64编码
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            throw new RuntimeException("生成许可证密钥失败", e);
        }
    }

    /**
     * 解析许可证密钥
     *
     * @param licenseKey 加密的许可证密钥
     * @return 包含许可证信息的Map，如果解析失败返回null
     */
    public static Map<String, Object> parseLicenseKey(String licenseKey) {
        try {
            // Base64解码
            byte[] decoded = Base64.getDecoder().decode(licenseKey);

            // AES解密
            String rawData = decrypt(decoded);

            // 分割数据
            String[] parts = rawData.split("\\" + SEPARATOR);
            if (parts.length != 4) {
                return null;
            }

            long activationTime = Long.parseLong(parts[0]);
            long expiryTime = Long.parseLong(parts[1]);
            int trialDays = Integer.parseInt(parts[2]);
            String checksum = parts[3];

            // 验证校验码
            String expectedChecksum = generateChecksum(activationTime, expiryTime, trialDays);
            if (!checksum.equals(expectedChecksum)) {
                return null;
            }

            // 返回解析结果
            Map<String, Object> result = new HashMap<>();
            result.put("activationTime", activationTime);
            result.put("expiryTime", expiryTime);
            result.put("trialDays", trialDays);

            return result;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 验证许可证密钥是否有效
     *
     * @param licenseKey 许可证密钥
     * @return true-有效，false-无效
     */
    public static boolean validateLicenseKey(String licenseKey) {
        if (licenseKey == null || licenseKey.trim().isEmpty()) {
            return false;
        }

        Map<String, Object> licenseInfo = parseLicenseKey(licenseKey);
        if (licenseInfo == null) {
            return false;
        }

        // 检查是否过期
        long expiryTime = (Long) licenseInfo.get("expiryTime");
        return System.currentTimeMillis() <= expiryTime;
    }

    /**
     * 获取许可证剩余天数
     *
     * @param licenseKey 许可证密钥
     * @return 剩余天数，如果已过期返回0，解析失败返回-1
     */
    public static int getRemainingDays(String licenseKey) {
        Map<String, Object> licenseInfo = parseLicenseKey(licenseKey);
        if (licenseInfo == null) {
            return -1;
        }

        long expiryTime = (Long) licenseInfo.get("expiryTime");
        long currentTime = System.currentTimeMillis();

        if (currentTime >= expiryTime) {
            return 0;
        }

        long remainingMillis = expiryTime - currentTime;
        return (int) (remainingMillis / (1000 * 60 * 60 * 24));
    }

    /**
     * AES加密
     */
    private static byte[] encrypt(String data) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(getAESKey(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * AES解密（从Base64字符串）
     *
     * @param encryptedKey Base64编码的加密字符串
     * @return 解密后的原始数据，失败返回null
     */
    public static String decrypt(String encryptedKey) {
        try {
            // Base64解码
            byte[] decoded = Base64.getDecoder().decode(encryptedKey);
            // AES解密
            return decrypt(decoded);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * AES解密（从字节数组）
     */
    private static String decrypt(byte[] data) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(getAESKey(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        byte[] decrypted = cipher.doFinal(data);
        return new String(decrypted, StandardCharsets.UTF_8);
    }

    /**
     * 获取AES密钥（16字节）
     */
    private static byte[] getAESKey() {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(SECRET_KEY.getBytes(StandardCharsets.UTF_8));
            return hash; // MD5产生16字节
        } catch (Exception e) {
            throw new RuntimeException("生成AES密钥失败", e);
        }
    }

    /**
     * 生成校验码（防止数据被篡改）- 公共方法
     *
     * @param activationTime 激活时间
     * @param expiryTime     到期时间
     * @param trialDays      试用天数
     * @return 校验码
     */
    public static String generateChecksum(long activationTime, long expiryTime, int trialDays) {
        try {
            String data = activationTime + "" + expiryTime + trialDays + SECRET_KEY;
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] hash = md.digest(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(hash).substring(0, 16);
        } catch (Exception e) {
            throw new RuntimeException("生成校验码失败", e);
        }
    }
}

