package com.ssyf.third.bmiss.core;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;

/**
 * 加解密管理类
 * 提供AES-CBC加密解密、双重MD5签名和公共参数管理功能
 * 完全对齐Python代码的加密流程和公共参数管理方式
 * 
 * @author system
 * @since 1.0
 */
@Slf4j
@Component
public class EncryptionManager {
    
    @Autowired
    private ConfigManager configManager;
    
    /** AES加密算法 */
    private static final String AES_ALGORITHM = "AES";
    
    /** AES/CBC/PKCS5Padding 模式 */
    private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    
    /** MD5算法 */
    private static final String MD5_ALGORITHM = "MD5";
    
    /** 默认IV向量（16字节） */
    private static final byte[] DEFAULT_IV = "0123456789abcdef".getBytes(StandardCharsets.UTF_8);
    
    /** 加密密钥 */
    private String secretKey;
    
    /** 是否启用加密 */
    private boolean encryptionEnabled = true;
    
    /** 公共参数管理器 */
    private CommonParamsManager commonParamsManager;
    
    /** 密钥映射 */
    private Map<String, SecretKeyInfo> secretKeysMap = new HashMap<>();
    
    /** 随机数生成器 */
    private Random random = new Random();
    
    /**
     * 初始化加密管理器
     */
    public void init() {
        // 初始化公共参数管理器
        this.commonParamsManager = new CommonParamsManager();
        
        // 初始化密钥映射
        initSecretKeys();
        
        log.debug("EncryptionManager初始化完成");
    }
    
    /**
     * 初始化密钥映射
     */
    private void initSecretKeys() {
        if (configManager != null && configManager.getEncryption() != null) {
            List<SecretConfig> secrets = configManager.getEncryption().getSecrets();
            if (secrets != null && !secrets.isEmpty()) {
                for (SecretConfig secret : secrets) {
                    // 使用密钥名称作为映射键
                    secretKeysMap.put(secret.getName(), new SecretKeyInfo(secret.getName() ,secret.getKey(), secret.getIv()));
                }
                log.debug("已加载{}个加密密钥", secrets.size());
            } else {
                // 如果没有配置多密钥，使用单个密钥配置
                String key = configManager.getEncryption().getKey();
                String iv = configManager.getEncryption().getIv();
                String name = configManager.getDefaultSecretKey();
                if (StrUtil.isNotBlank(name) && StrUtil.isNotBlank(key)) {
                    secretKeysMap.put(name, new SecretKeyInfo(name ,key, iv));
                    log.debug("已加载默认加密密钥");
                }
            }
        }
    }
    
    /**
     * 设置加密密钥
     * 
     * @param secretKey 加密密钥
     */
    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }
    
    /**
     * 设置是否启用加密
     * 
     * @param encryptionEnabled 是否启用加密
     */
    public void setEncryptionEnabled(boolean encryptionEnabled) {
        this.encryptionEnabled = encryptionEnabled;
    }
    
    /**
     * 设置配置管理器
     * 
     * @param configManager 配置管理器
     */
    public void setConfigManager(ConfigManager configManager) {
        this.configManager = configManager;
    }
    
    /**
     * 随机选择一个加密密钥
     * 
     * @return 密钥信息
     */
    public SecretKeyInfo selectRandomSecretKey() {
        if (secretKeysMap.isEmpty()) {
            throw new IllegalStateException("没有可用的加密密钥");
        }
        
        // 获取所有密钥名称
        String[] keyNames = secretKeysMap.keySet().toArray(new String[0]);
        int randomIndex = random.nextInt(keyNames.length);
        return secretKeysMap.get(keyNames[randomIndex]);
    }
    
    /**
     * 根据密钥名称获取密钥
     * 
     * @param name 密钥名称
     * @return 密钥信息
     */
    public SecretKeyInfo getSecretKeyByName(String name) {
        return secretKeysMap.get(name);
    }
    
    /**
     * 获取默认密钥
     * 
     * @return 默认密钥信息
     */
    public SecretKeyInfo getDefaultSecretKey() {
        // 优先使用配置的默认密钥
        if (configManager != null && configManager.getEncryption() != null) {
            String defaultKeyName = configManager.getEncryption().getDefaultKey();
            if (StrUtil.isNotBlank(defaultKeyName)) {
                SecretKeyInfo secretKey = secretKeysMap.get(defaultKeyName);
                if (secretKey != null) {
                    return secretKey;
                }
            }
        }
        
        // 如果没有找到默认密钥，使用第一个密钥
        if (!secretKeysMap.isEmpty()) {
            return secretKeysMap.values().iterator().next();
        }
        
        throw new IllegalStateException("没有可用的加密密钥");
    }
    
    /**
     * 获取所有可用的密钥名称
     * 
     * @return 密钥名称列表
     */
    public List<String> getAvailableSecretNames() {
        return new ArrayList<>(secretKeysMap.keySet());
    }
    
    /**
     * 获取签名密钥
     * 
     * @return 签名密钥
     */
    private String getSignatureKey() {
        if (configManager != null && configManager.getEncryption() != null) {
            return configManager.getEncryption().getSignatureKey();
        }
        return "asdasgfdwqew";
    }
    
    /**
     * AES-CBC加密
     * 
     * @param plainText 明文
     * @return 加密后的Base64字符串
     */
    public String aesEncrypt(String plainText) {
        return aesEncrypt(plainText, null);
    }
    
    /**
     * AES-CBC加密（使用指定密钥）
     * 
     * @param plainText 明文
     * @param secretKeyInfo 密钥信息
     * @return 加密后的Base64字符串
     */
    public String aesEncrypt(String plainText, SecretKeyInfo secretKeyInfo) {
        if (!encryptionEnabled || StrUtil.isBlank(plainText)) {
            return plainText;
        }
        
        String keyToUse;
        if (secretKeyInfo != null) {
            keyToUse = secretKeyInfo.getKey();
        } else if (StrUtil.isNotBlank(secretKey)) {
            keyToUse = secretKey;
        } else {
            throw new IllegalArgumentException("加密密钥不能为空");
        }
        
        try {
            // 确保密钥长度符合AES要求（16, 24, 32字节）
            byte[] keyBytes = ensureAESKeyLength(keyToUse.getBytes(StandardCharsets.UTF_8));
            byte[] ivBytes = secretKeyInfo != null ? secretKeyInfo.getIvBytes() : DEFAULT_IV;
            
            AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, keyBytes, ivBytes);
            // 在加密完成后添加URL编码
            String base64Encoded = aes.encryptBase64(plainText);
            return URLEncoder.encode(base64Encoded, StandardCharsets.UTF_8.name());
        } catch (Exception e) {
            log.error("AES加密失败: {}", e.getMessage(), e);
            throw new RuntimeException("AES加密失败", e);
        }
    }
    
    /**
     * AES-CBC解密
     * 
     * @param encryptedText 加密后的Base64字符串
     * @return 解密后的明文
     */
    public String aesDecrypt(String encryptedText) {
        return aesDecrypt(encryptedText, (SecretKeyInfo) null);
    }
    
    /**
     * AES-CBC解密（使用指定密钥）
     * 
     * @param encryptedText 加密后的Base64字符串
     * @param secretKeyInfo 密钥信息
     * @return 解密后的明文
     */
    public String aesDecrypt(String encryptedText, SecretKeyInfo secretKeyInfo) {
        if (!encryptionEnabled || StrUtil.isBlank(encryptedText)) {
            return encryptedText;
        }
        
        String keyToUse;
        if (secretKeyInfo != null) {
            keyToUse = secretKeyInfo.getKey();
        } else if (StrUtil.isNotBlank(secretKey)) {
            keyToUse = secretKey;
        } else {
            throw new IllegalArgumentException("加密密钥不能为空");
        }
        
        try {
            // 确保密钥长度符合AES要求（16, 24, 32字节）
            byte[] keyBytes = ensureAESKeyLength(keyToUse.getBytes(StandardCharsets.UTF_8));
            byte[] ivBytes = secretKeyInfo != null ? secretKeyInfo.getIvBytes() : DEFAULT_IV;
            
            AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, keyBytes, ivBytes);
            return aes.decryptStr(encryptedText);
        } catch (Exception e) {
            log.error("AES解密失败: {}", e.getMessage(), e);
            throw new RuntimeException("AES解密失败", e);
        }
    }
    
    /**
     * AES-CBC解密（使用密钥名称，对齐Python代码的解密逻辑）
     * 
     * @param encryptedText 加密后的Base64字符串
     * @param secretKeyName 密钥名称
     * @return 解密后的明文
     */
    public String aesDecrypt(String encryptedText, String secretKeyName) {
        if (!encryptionEnabled || StrUtil.isBlank(encryptedText)) {
            return encryptedText;
        }
        
        try {
            // 获取对应的密钥配置
            SecretKeyInfo secretKeyInfo = getSecretKeyByName(secretKeyName);
            if (secretKeyInfo != null) {
                return aesDecrypt(encryptedText, secretKeyInfo);
            } else {
                // 尝试所有密钥进行解密（与Python代码保持一致）
                return decryptWithAllKeys(encryptedText);
            }
        } catch (Exception e) {
            log.error("使用密钥名称解密失败: {}", e.getMessage(), e);
            throw new RuntimeException("使用密钥名称解密失败", e);
        }
    }
    
    /**
     * 尝试所有密钥进行解密（对齐Python代码的_decrypt_with_all_keys方法）
     * 
     * @param encryptedText 加密后的Base64字符串
     * @return 解密后的明文
     */
    private String decryptWithAllKeys(String encryptedText) {
        for (String secretName : secretKeysMap.keySet()) {
            try {
                return aesDecrypt(encryptedText, secretName);
            } catch (Exception e) {
                log.debug("使用密钥 {} 解密失败，尝试下一个密钥", secretName);
                continue;
            }
        }
        
        // 如果所有密钥都失败，返回原始数据（与Python代码保持一致）
        log.warn("所有密钥解密失败，返回原始数据");
        return encryptedText;
    }
    
    /**
     * 双重MD5签名（对齐Python代码的双重MD5逻辑）
     * 
     * @param data 待签名的数据
     * @param signKey 签名密钥
     * @return 双重MD5签名
     */
    public String doubleMd5Sign(String data, String signKey) {
        if (StrUtil.isBlank(data)) {
            return "";
        }
        
        // 第一次MD5：对原始数据签名
        String firstMd5 = md5(data);
        
        // 第二次MD5：对第一次签名结果和签名密钥组合进行签名
        String combined = firstMd5 + signKey;
        String secondMd5 = md5(combined);
        
        log.debug("双重MD5签名 - 原始数据: {}, 第一次MD5: {}, 组合数据: {}, 最终签名: {}", 
                 data, firstMd5, combined, secondMd5);
        
        return secondMd5;
    }
    
    /**
     * 获取公共参数（对齐Python代码的公共参数管理）
     * 
     * @return 公共参数Map
     */
    public Map<String, Object> getCommonParams() {
        if (commonParamsManager == null) {
            throw new IllegalStateException("公共参数管理器未初始化");
        }
        return commonParamsManager.getCommonParams();
    }
    
    /**
     * 获取带时间戳的公共参数（对齐Python代码）
     * 
     * @return 带时间戳的公共参数Map
     */
    public Map<String, Object> getCommonParamsWithTimestamp() {
        if (commonParamsManager == null) {
            throw new IllegalStateException("公共参数管理器未初始化");
        }
        return commonParamsManager.getCommonParamsWithTimestamp();
    }
    
    /**
     * 获取当前使用的密钥数量
     * 
     * @return 密钥数量
     */
    public int getSecretKeyCount() {
        return secretKeysMap.size();
    }
    
    /**
     * 获取公共参数管理器
     * 
     * @return 公共参数管理器
     */
    public CommonParamsManager getCommonParamsManager() {
        return commonParamsManager;
    }
    
    /**
     * 生成公共参数
     * 包含时间戳和双重MD5签名
     * 
     * @param data 待签名的数据
     * @return 公共参数Map
     */
    public Map<String, String> generateCommonParams(String data) {
        Map<String, String> params = new HashMap<>();
        
        // 时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        params.put("timestamp", String.valueOf(timestamp));
        
        // 双重MD5签名
        if (StrUtil.isNotBlank(data)) {
            String signKey = getSignatureKey();
            String sign = doubleMd5Sign(data + timestamp, signKey);
            params.put("sign", sign);
        }
        
        return params;
    }
    
    /**
     * 验证签名
     * 
     * @param data 原始数据
     * @param timestamp 时间戳
     * @param sign 待验证的签名
     * @return 验证结果
     */
    public boolean verifySign(String data, String timestamp, String sign) {
        if (StrUtil.isBlank(data) || StrUtil.isBlank(timestamp) || StrUtil.isBlank(sign)) {
            return false;
        }
        
        try {
            String signKey = getSignatureKey();
            String expectedSign = doubleMd5Sign(data + timestamp, signKey);
            return sign.equals(expectedSign);
        } catch (Exception e) {
            log.error("签名验证失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 简单的MD5方法（用于双重MD5签名）
     * 
     * @param data 待签名数据
     * @return MD5签名
     */
    private String md5(String data) {
        try {
            MessageDigest md = MessageDigest.getInstance(MD5_ALGORITHM);
            byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));
            return HexUtil.encodeHexStr(digest).toLowerCase();
        } catch (Exception e) {
            log.error("MD5计算失败: {}", e.getMessage(), e);
            throw new RuntimeException("MD5计算失败", e);
        }
    }
    
    /**
     * 生成签名（对齐Python代码的generate_signature_api_style方法）
     * 对参数进行双重MD5签名，与API签名方式保持一致
     * 
     * @param params 参数Map
     * @return 双重MD5签名字符串
     */
    public String generateSignature(Map<String, Object> params) {
        if (params == null || params.isEmpty()) {
            return "";
        }
        
        try {
            // 参数排序（按字典序）
            List<String> sortedKeys = new ArrayList<>(params.keySet());
            Collections.sort(sortedKeys);
            
            // 拼接参数值
            StringBuilder signatureStr = new StringBuilder();
            for (String key : sortedKeys) {
                Object value = params.get(key);
                if (value != null) {
                    if (value instanceof Map) {
                        // 如果是Map类型，转换为JSON字符串
                        signatureStr.append(JSONUtil.toJsonStr(value));
                    } else {
                        signatureStr.append(value.toString());
                    }
                }
            }
            
            // 添加签名密钥
            String signKey = getSignatureKey();
            signatureStr.append(signKey);
            
            // 双重MD5加密
            String firstMd5 = md5(signatureStr.toString());
            String secondMd5 = md5(firstMd5);
            
            log.debug("API风格签名生成 - 参数数量: {}, 签名字符串: {}, 最终签名: {}", 
                     params.size(), signatureStr.toString(), secondMd5);
            
            return secondMd5;
        } catch (Exception e) {
            log.error("生成签名失败: {}", e.getMessage(), e);
            return "";
        }
    }
    
    /**
     * 确保AES密钥长度符合要求（16, 24, 32字节）
     * 如果密钥长度不符合，则进行填充或截断
     * 
     * @param keyBytes 原始密钥字节数组
     * @return 符合要求的密钥字节数组
     */
    private byte[] ensureAESKeyLength(byte[] keyBytes) {
        int keyLength = keyBytes.length;
        
        // AES密钥长度要求：16, 24, 32字节
        if (keyLength == 16 || keyLength == 24 || keyLength == 32) {
            return keyBytes;
        }
        
        // 如果密钥长度小于16字节，则用0填充
        if (keyLength < 16) {
            byte[] paddedKey = new byte[16];
            System.arraycopy(keyBytes, 0, paddedKey, 0, keyLength);
            return paddedKey;
        }
        
        // 如果密钥长度大于32字节，则截断
        if (keyLength > 32) {
            byte[] truncatedKey = new byte[32];
            System.arraycopy(keyBytes, 0, truncatedKey, 0, 32);
            return truncatedKey;
        }
        
        // 对于16-32之间的中间长度，填充到下一个标准长度
        if (keyLength < 24) {
            byte[] paddedKey = new byte[24];
            System.arraycopy(keyBytes, 0, paddedKey, 0, keyLength);
            return paddedKey;
        } else {
            byte[] paddedKey = new byte[32];
            System.arraycopy(keyBytes, 0, paddedKey, 0, keyLength);
            return paddedKey;
        }
    }
    
    /**
     * 密钥信息类
     */
    @Data
    @Accessors(chain = true)
    public static class SecretKeyInfo {
        private String name;
        private String key;
        private String iv;
        private int index;
        
        public SecretKeyInfo(String name,String key, String iv) {
            this.name = name;
            this.key = key;
            this.iv = iv;
            this.index = 0;
        }
        
        public SecretKeyInfo(String name ,String key, String iv, int index) {
            this.name = this.name;
            this.key = key;
            this.iv = iv;
            this.index = index;
        }
        
        /**
         * 获取IV字节数组
         * 
         * @return IV字节数组
         */
        public byte[] getIvBytes() {
            if (StrUtil.isNotBlank(iv)) {
                return iv.getBytes(StandardCharsets.UTF_8);
            }
            return DEFAULT_IV;
        }
    }
    
    /**
     * 公共参数管理器（对齐Python代码的公共参数管理）
     */
    public static class CommonParamsManager {
        
        /**
         * 获取公共参数
         * 
         * @return 公共参数Map
         */
        public Map<String, Object> getCommonParams() {
            Map<String, Object> params = new HashMap<>();
            
            // 添加公共参数（根据Python代码中的公共参数）
            params.put("appId", "bmiss");
            params.put("version", "1.0");
            params.put("timestamp", System.currentTimeMillis());
            
            return params;
        }
        
        /**
         * 获取带额外时间戳的公共参数
         * 
         * @return 带时间戳的公共参数Map
         */
        public Map<String, Object> getCommonParamsWithTimestamp() {
            Map<String, Object> params = getCommonParams();
            // 添加额外的时间戳参数
            params.put("requestTime", System.currentTimeMillis());
            return params;
        }
    }
}