/**
 * AES-256-GCM 加密工具（兼容 H5/小程序/App）
 * 与后端 AesEncryptUtil 保持一致
 * 
 * 使用场景：
 * - 密码传输前加密
 * - 敏感信息（手机号、身份证号）加密
 * 
 * @author yslg
 * @since 2025-12-10
 */

/**
 * AES 加密配置
 */
interface AesConfig {
    algorithm: string
    keySize: number
    ivLength: number
    tagLength: number
}

const AES_CONFIG: AesConfig = {
    algorithm: 'AES-GCM',
    keySize: 256,
    ivLength: 12,  // GCM 推荐 IV 长度
    tagLength: 128 // GCM 认证标签长度
}

const getCrypto = (): Crypto => {
    return window.crypto
}

/**
 * AES 加密工具类
 */
export class AesEncryptUtil {
    private secretKey: CryptoKey | null = null
    private secretKeyString: string

    /**
     * 构造函数
     * @param secretKeyString Base64 编码的密钥（32字节，AES-256）
     */
    constructor(secretKeyString: string) {
        this.secretKeyString = secretKeyString
    }

    /**
     * 初始化密钥（异步）
     */
    async init(): Promise<void> {
        if (!this.secretKeyString) {
            throw new Error('AES 密钥未配置')
        }

        try {
            // Base64 解码密钥
            const keyData = this.base64ToArrayBuffer(this.secretKeyString)
            
            // 校验密钥长度（AES-256 必须是 32 字节）
            if (keyData.byteLength !== 32) {
                throw new Error(`AES 密钥长度错误：需32字节，实际${keyData.byteLength}字节`)
            }

            // 导入密钥（跨端兼容）
            this.secretKey = await getCrypto().subtle.importKey(
                'raw',
                keyData,
                { name: AES_CONFIG.algorithm },
                false,
                ['encrypt', 'decrypt']
            )
        } catch (error) {
            throw new Error(`AES 密钥初始化失败: ${error}`)
        }
    }

    /**
     * 加密字符串
     * @param plaintext 明文
     * @returns Base64 编码的密文（包含 IV）
     */
    async encrypt(plaintext: string): Promise<string> {
        if (!plaintext) {
            return plaintext
        }

        if (!this.secretKey) {
            throw new Error('AES 密钥未初始化，请先调用 init()')
        }

        try {
            // 生成随机 IV（跨端兼容）
            const iv = getCrypto().getRandomValues(new Uint8Array(AES_CONFIG.ivLength))

            // 将明文转为 ArrayBuffer
            const encoder = new TextEncoder()
            const data = encoder.encode(plaintext)

            // 加密
            const ciphertext = await getCrypto().subtle.encrypt(
                {
                    name: AES_CONFIG.algorithm,
                    iv: iv,
                    tagLength: AES_CONFIG.tagLength
                },
                this.secretKey,
                data
            )

            // 将 IV 和密文组合：[IV(12字节)][密文]
            const combined = new Uint8Array(iv.length + ciphertext.byteLength)
            combined.set(iv, 0)
            combined.set(new Uint8Array(ciphertext), iv.length)

            // Base64 编码
            return this.arrayBufferToBase64(combined)
        } catch (error) {
            throw new Error(`AES 加密失败: ${error}`)
        }
    }

    /**
     * 解密字符串
     * @param ciphertext Base64 编码的密文（包含 IV）
     * @returns 明文
     */
    async decrypt(ciphertext: string): Promise<string> {
        if (!ciphertext) {
            return ciphertext
        }

        if (!this.secretKey) {
            throw new Error('AES 密钥未初始化，请先调用 init()')
        }

        try {
            // Base64 解码
            const combinedBuffer = this.base64ToArrayBuffer(ciphertext)
            const combined = new Uint8Array(combinedBuffer)

            // 校验 IV 长度
            if (combined.length < AES_CONFIG.ivLength) {
                throw new Error('密文格式错误：IV 长度不足')
            }

            // 提取 IV 和密文
            const iv = combined.slice(0, AES_CONFIG.ivLength)
            const data = combined.slice(AES_CONFIG.ivLength)

            // 解密
            const plaintext = await getCrypto().subtle.decrypt(
                {
                    name: AES_CONFIG.algorithm,
                    iv: iv,
                    tagLength: AES_CONFIG.tagLength
                },
                this.secretKey,
                data
            )

            // 将 ArrayBuffer 转为字符串
            const decoder = new TextDecoder()
            return decoder.decode(plaintext)
        } catch (error) {
            throw new Error(`AES 解密失败: ${error}`)
        }
    }

    /**
     * 加密密码（用于登录等场景）
     */
    async encryptPassword(password: string): Promise<string> {
        return this.encrypt(password)
    }

    /**
     * 加密手机号
     */
    async encryptPhone(phone: string): Promise<string> {
        return this.encrypt(phone)
    }

    /**
     * 解密手机号
     */
    async decryptPhone(encryptedPhone: string): Promise<string> {
        return this.decrypt(encryptedPhone)
    }

    /**
     * 加密身份证号
     */
    async encryptIdCard(idCard: string): Promise<string> {
        return this.encrypt(idCard)
    }

    /**
     * 解密身份证号
     */
    async decryptIdCard(encryptedIdCard: string): Promise<string> {
        return this.decrypt(encryptedIdCard)
    }

    // ============ 工具方法 ============

    /**
     * Base64 转 ArrayBuffer
     */
    private base64ToArrayBuffer(base64: string): ArrayBuffer {
        // 处理 Base64 填充字符
        base64 = base64.replace(/-/g, '+').replace(/_/g, '/')
        const padLength = (4 - (base64.length % 4)) % 4
        base64 += '='.repeat(padLength)
        
        const binaryString = atob(base64)
        const bytes = new Uint8Array(binaryString.length)
        for (let i = 0; i < binaryString.length; i++) {
            bytes[i] = binaryString.charCodeAt(i)
        }
        return bytes.buffer
    }

    /**
     * ArrayBuffer 转 Base64
     */
    private arrayBufferToBase64(buffer: Uint8Array): string {
        let binary = ''
        const len = buffer.byteLength
        for (let i = 0; i < len; i++) {
            binary += String.fromCharCode(buffer[i])
        }
        return btoa(binary)
    }
}

/**
 * 静态工具方法
 */
export class AesUtils {
    /**
     * 脱敏显示手机号
     * 例如：13812345678 -> 138****5678
     */
    static maskPhone(phone: string): string {
        if (!phone || phone.length < 11) {
            return phone
        }
        return phone.substring(0, 3) + '****' + phone.substring(7)
    }

    /**
     * 脱敏显示身份证号
     * 例如：110101199001011234 -> 110101********1234
     */
    static maskIdCard(idCard: string): string {
        if (!idCard || idCard.length < 18) {
            return idCard
        }
        return idCard.substring(0, 6) + '********' + idCard.substring(14)
    }

    /**
     * 脱敏显示邮箱
     * 例如：test@example.com -> t***@example.com
     */
    static maskEmail(email: string): string {
        if (!email || !email.includes('@')) {
            return email
        }
        const [username, domain] = email.split('@')
        if (username.length <= 1) {
            return email
        }
        return username[0] + '***@' + domain
    }

    /**
     * 生成 AES-256 Base64 密钥（工具方法，用于后端/测试）
     */
    static generateAes256KeyBase64(): string {
        const crypto = getCrypto()
        const keyBytes = crypto.getRandomValues(new Uint8Array(32)) // 32字节=256位
        let binary = ''
        for (let i = 0; i < keyBytes.length; i++) {
            binary += String.fromCharCode(keyBytes[i])
        }
        return btoa(binary)
    }
}

/**
 * 创建 AES 加密实例的工厂函数
 * @param secretKey Base64 编码的密钥（32字节）
 */
export async function createAesEncrypt(secretKey: string): Promise<AesEncryptUtil> {
    const aes = new AesEncryptUtil(secretKey)
    await aes.init()
    return aes
}

// 导出单例（需要在应用启动时初始化）
let aesInstance: AesEncryptUtil | null = null

/**
 * 获取 AES 加密实例
 */
export function getAesInstance(): AesEncryptUtil {
    if (!aesInstance) {
        throw new Error('AES 加密工具未初始化，请先调用 initAesEncrypt()')
    }
    return aesInstance
}

/**
 * 初始化 AES 加密工具（在应用启动时调用）
 * @param secretKey Base64 编码的密钥（32字节，与后端配置保持一致）
 */
export async function initAesEncrypt(secretKey: string): Promise<void> {
    aesInstance = await createAesEncrypt(secretKey)
}