import AES from 'crypto-js/aes';
import 'crypto-js/cipher-core';
import CryptoJS from 'crypto-js/core';
import encBase64 from 'crypto-js/enc-base64';
import encUtf8 from 'crypto-js/enc-utf8';
import 'crypto-js/lib-typedarrays';
import MD5 from 'crypto-js/md5';
import modeCtr from 'crypto-js/mode-ctr';
import modeEcb from 'crypto-js/mode-ecb';
import Rabbit from 'crypto-js/rabbit';
import SHA1 from 'crypto-js/sha1';
import SHA256 from 'crypto-js/sha256';

import { bigEndianToLittleEndian, littleEndianToBigEndian } from '@/utils/bytesUtils';

import { toBase64, fromBase64 } from './base64url';

// ---------------------------------------------------------------------------
// 常量
// ---------------------------------------------------------------------------

const WordArray = CryptoJS.lib.WordArray as typeof CryptoJS.lib.WordArray & {
    create(words?: number[] | Uint8Array, sigBytes?: number): CryptoJS.lib.WordArray;
};
const CipherParams = CryptoJS.lib.CipherParams as {
    create(cfg: {
        ciphertext: CryptoJS.lib.WordArray;
        iv?: CryptoJS.lib.WordArray;
    }): CryptoJS.lib.CipherParams;
};

const KEY_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const KEY_CHARS_LEN = KEY_CHARS.length;
const MAX_ALLOWED = 256 - (256 % KEY_CHARS_LEN);

const AES_KEY_BYTES = 32;
const AES_IV_BYTES = 16;
const RABBIT_KEY_BYTES = 16;
const RABBIT_IV_BYTES = 8;

// ---------------------------------------------------------------------------
// 内部工具函数
// ---------------------------------------------------------------------------

function wordsToBytes(words: number[], sigBytes: number, littleEndian?: boolean): Uint8Array {
    const out = new Uint8Array(sigBytes);
    for (let i = 0; i < sigBytes; i++) {
        out[i] = (words[i >>> 2]! >>> (24 - (i % 4) * 8)) & 0xff;
    }
    return littleEndian ? bigEndianToLittleEndian(out) : out;
}

function bytesToWordArray(bytes: Uint8Array, littleEndian?: boolean): CryptoJS.lib.WordArray {
    const input = littleEndian ? littleEndianToBigEndian(bytes) : bytes;
    return WordArray.create(input);
}

function wordArrayToBytes(wa: CryptoJS.lib.WordArray, littleEndian?: boolean): Uint8Array {
    return wordsToBytes(wa.words, wa.sigBytes, littleEndian);
}

function padBytes(buf: Uint8Array, len: number): Uint8Array {
    if (buf.length >= len) return buf.subarray(0, len);
    const out = new Uint8Array(len);
    out.set(buf);
    return out;
}

/**
 * 计算字符串的 MD5
 */
export function stringMd5(str: string, lowerCase?: boolean): string {
    return lowerCase ? String(MD5(str)).toLowerCase() : String(MD5(str)).toUpperCase();
}

/**
 * 计算二进制数据的 MD5，返回 16 字节
 */
export function bytesMd5(bytes: Uint8Array): Uint8Array {
    return wordArrayToBytes(MD5(bytesToWordArray(bytes)) as CryptoJS.lib.WordArray);
}

/**
 * 计算字符串的 SHA-1
 */
export function stringSha1(str: string, lowerCase?: boolean): string {
    return lowerCase ? String(SHA1(str)).toLowerCase() : String(SHA1(str)).toUpperCase();
}

/**
 * 计算二进制数据的 SHA-1，返回 20 字节
 */
export function bytesSha1(bytes: Uint8Array): Uint8Array {
    return wordArrayToBytes(SHA1(bytesToWordArray(bytes)) as CryptoJS.lib.WordArray);
}

/**
 * 计算字符串的 SHA-256
 */
export function stringSha256(str: string, lowerCase?: boolean): string {
    return lowerCase ? String(SHA256(str)).toLowerCase() : String(SHA256(str)).toUpperCase();
}

/**
 * 计算二进制数据的 SHA-256，返回 32 字节
 */
export function bytesSha256(bytes: Uint8Array): Uint8Array {
    return wordArrayToBytes(SHA256(bytesToWordArray(bytes)) as CryptoJS.lib.WordArray);
}

/**
 * 将字符串编码为 Base64 字符串
 */
export function stringBase64encode(raw: string, urlSafe = false): string {
    try {
        const out = encBase64.stringify(encUtf8.parse(raw));
        if (urlSafe) return fromBase64(out);
        return out;
    } catch {
        return '';
    }
}

/**
 * 对二进制数据进行 Base64 编码
 */
export function bytesBase64encode(bytes: Uint8Array, urlSafe = false): string {
    try {
        const out = encBase64.stringify(bytesToWordArray(bytes));
        if (urlSafe) return fromBase64(out);
        return out;
    } catch {
        return '';
    }
}

/**
 * 将 Base64 字符串解码为字符串
 */
export function stringBase64decode(str: string, urlSafe = false): string {
    try {
        const input = urlSafe ? toBase64(str) : str;
        return encBase64.parse(input).toString(encUtf8);
    } catch {
        return '';
    }
}

/**
 * 将 Base64 字符串解码为二进制数据
 */
export function bytesBase64decode(str: string, urlSafe = false): Uint8Array {
    try {
        const input = urlSafe ? toBase64(str) : str;
        return wordArrayToBytes(encBase64.parse(input));
    } catch {
        return new Uint8Array(0);
    }
}

/**
 * 生成随机密钥 (字符串)
 * @param length 密钥长度
 * @returns 随机密钥
 */
export const generateKeyString = (length: number = 32): string => {
    if (length <= 0) return '';

    const result: string[] = [];
    const bufferSize = Math.ceil((length * 256) / MAX_ALLOWED) + 16;
    const buffer = new Uint8Array(bufferSize);
    let pos = buffer.length;
    while (result.length < length) {
        if (pos >= buffer.length) {
            crypto.getRandomValues(buffer);
            pos = 0;
        }
        const byte = buffer[pos++];
        if (byte < MAX_ALLOWED) {
            result.push(KEY_CHARS[byte % KEY_CHARS_LEN]);
        }
    }
    return result.join('');
};

/**
 * 生成随机密钥 (二进制)
 * @param length 密钥长度
 * @returns 随机密钥
 */
export const generateKeyBytes = (length: number): Uint8Array => {
    const out = new Uint8Array(length);
    crypto.getRandomValues(out);
    return out;
};

/**
 * AES-CBC 加密（字符串）
 *
 * 注意：
 *   - 如果如果 iv 为空，则返回的加密结果的头部会包含 iv，否则不包含 iv。
 *   - 如果 key 的长度不足 32 位，则会在末尾补齐 字符'0'。
 *   - 如果 iv 的长度不足 16 位，则会在末尾补齐 字符'0'。
 */
export const aesCbcEncryptString = (data: string, key: string, iv?: string): string => {
    if (!data || !key) return '';
    const paddedKey = key.padEnd(32, '0').slice(0, 32);
    const ivValue = iv ? iv.padEnd(16, '0').slice(0, 16) : generateKeyString(16);
    const keyHex = encUtf8.parse(paddedKey);
    const ivHex = encUtf8.parse(ivValue);
    const encrypted = AES.encrypt(data, keyHex, { iv: ivHex });
    const ciphertextBase64 = encBase64.stringify(encrypted.ciphertext);
    return iv ? ciphertextBase64 : ivValue + ciphertextBase64;
};

/**
 * AES-CBC 解密（字符串），对应 aesCbcEncryptString
 */
export const aesCbcDecryptString = (encryptedData: string, key: string, iv?: string): string => {
    if (!encryptedData || !key) return '';
    try {
        const paddedKey = key.padEnd(32, '0').slice(0, 32);
        const ivValue = iv ? iv.padEnd(16, '0').slice(0, 16) : encryptedData.slice(0, 16);
        const ciphertextBase64 = iv ? encryptedData : encryptedData.slice(16);
        const keyHex = encUtf8.parse(paddedKey);
        const ivHex = encUtf8.parse(ivValue);
        const cp = CipherParams.create({ ciphertext: encBase64.parse(ciphertextBase64) });
        const decrypted = AES.decrypt(cp, keyHex, { iv: ivHex });
        return decrypted.toString(encUtf8);
    } catch (error) {
        console.error('AES decrypt error:', error);
        return '';
    }
};

/**
 * AES-CBC 加密（二进制）
 *
 * 注意：
 *   - 如果如果 iv 为空，则返回的加密结果的头部会包含 iv，否则不包含 iv。
 *   - 如果 key 的长度不足 32 位，则会在末尾补齐 数字0。
 *   - 如果 iv 的长度不足 16 位，则会在末尾补齐 数字0。
 */
export function aesCbcEncryptBytes(data: Uint8Array, key: Uint8Array, iv?: Uint8Array): Uint8Array {
    if (!data.length || !key.length) return new Uint8Array(0);
    const keyWa = bytesToWordArray(padBytes(key, AES_KEY_BYTES));
    const ivWa = iv
        ? bytesToWordArray(padBytes(iv, AES_IV_BYTES))
        : bytesToWordArray(generateKeyBytes(AES_IV_BYTES));
    const encrypted = AES.encrypt(bytesToWordArray(data), keyWa, { iv: ivWa });
    const ct = wordArrayToBytes(encrypted.ciphertext);
    if (iv) return ct;
    const out = new Uint8Array(AES_IV_BYTES + ct.length);
    out.set(wordArrayToBytes(ivWa), 0);
    out.set(ct, AES_IV_BYTES);
    return out;
}

/**
 * AES-CBC 解密（二进制）对应 aesCbcEncryptBytes
 */
export function aesCbcDecryptBytes(
    encryptedData: Uint8Array,
    key: Uint8Array,
    iv?: Uint8Array
): Uint8Array {
    if (!encryptedData.length || !key.length) return new Uint8Array(0);
    try {
        const ivBytes = iv ? padBytes(iv, AES_IV_BYTES) : encryptedData.subarray(0, AES_IV_BYTES);
        const ctBytes = iv ? encryptedData : encryptedData.subarray(AES_IV_BYTES);
        const keyWa = bytesToWordArray(padBytes(key, AES_KEY_BYTES));
        const ivWa = bytesToWordArray(ivBytes);
        const cp = CipherParams.create({ ciphertext: bytesToWordArray(ctBytes), iv: ivWa });
        const decrypted = AES.decrypt(cp, keyWa, { iv: ivWa });
        return wordArrayToBytes(decrypted);
    } catch (error) {
        console.error('AES-CBC decrypt bytes error:', error);
        return new Uint8Array(0);
    }
}

/**
 * AES-CTR 加密（字符串）
 *
 * 注意：
 *   - 如果如果 iv 为空，则返回的加密结果的头部会包含 iv，否则不包含 iv。
 *   - 如果 key 的长度不足 32 位，则会在末尾补齐 字符'0'。
 *   - 如果 iv 的长度不足 16 位，则会在末尾补齐 字符'0'。
 */
export const aesCtrEncryptString = (data: string, key: string, iv?: string): string => {
    if (!data || !key) return '';
    const paddedKey = key.padEnd(32, '0').slice(0, 32);
    const ivValue = iv ? iv.padEnd(16, '0').slice(0, 16) : generateKeyString(16);
    const keyHex = encUtf8.parse(paddedKey);
    const ivHex = encUtf8.parse(ivValue);
    const encrypted = AES.encrypt(data, keyHex, { iv: ivHex, mode: modeCtr });
    const ciphertextBase64 = encBase64.stringify(encrypted.ciphertext);
    return iv ? ciphertextBase64 : ivValue + ciphertextBase64;
};

/**
 * AES-CTR 解密（字符串）对应 aesCtrEncryptString
 */
export const aesCtrDecryptString = (encryptedData: string, key: string, iv?: string): string => {
    if (!encryptedData || !key) return '';
    try {
        const paddedKey = key.padEnd(32, '0').slice(0, 32);
        const ivValue = iv ? iv.padEnd(16, '0').slice(0, 16) : encryptedData.slice(0, 16);
        const ciphertextBase64 = iv ? encryptedData : encryptedData.slice(16);
        const keyHex = encUtf8.parse(paddedKey);
        const ivHex = encUtf8.parse(ivValue);
        const cp = CipherParams.create({ ciphertext: encBase64.parse(ciphertextBase64) });
        const decrypted = AES.decrypt(cp, keyHex, { iv: ivHex, mode: modeCtr });
        return decrypted.toString(encUtf8);
    } catch (error) {
        console.error('AES-CTR decrypt error:', error);
        return '';
    }
};

/**
 * AES-CTR 加密（二进制）
 *
 * 注意：
 *   - 如果如果 iv 为空，则返回的加密结果的头部会包含 iv，否则不包含 iv。
 *   - 如果 key 的长度不足 32 位，则会在末尾补齐 数字0。
 *   - 如果 iv 的长度不足 16 位，则会在末尾补齐 数字0。
 */
export function aesCtrEncryptBytes(data: Uint8Array, key: Uint8Array, iv?: Uint8Array): Uint8Array {
    if (!data.length || !key.length) return new Uint8Array(0);
    const keyWa = bytesToWordArray(padBytes(key, AES_KEY_BYTES));
    const ivWa = iv
        ? bytesToWordArray(padBytes(iv, AES_IV_BYTES))
        : bytesToWordArray(generateKeyBytes(AES_IV_BYTES));
    const encrypted = AES.encrypt(bytesToWordArray(data), keyWa, { iv: ivWa, mode: modeCtr });
    const ct = wordArrayToBytes(encrypted.ciphertext);
    if (iv) return ct;
    const out = new Uint8Array(AES_IV_BYTES + ct.length);
    out.set(wordArrayToBytes(ivWa), 0);
    out.set(ct, AES_IV_BYTES);
    return out;
}

/**
 * AES-CTR 解密（二进制）
 */
export function aesCtrDecryptBytes(
    encryptedData: Uint8Array,
    key: Uint8Array,
    iv?: Uint8Array
): Uint8Array {
    if (!encryptedData.length || !key.length) return new Uint8Array(0);
    try {
        const ivBytes = iv ? padBytes(iv, AES_IV_BYTES) : encryptedData.subarray(0, AES_IV_BYTES);
        const ctBytes = iv ? encryptedData : encryptedData.subarray(AES_IV_BYTES);
        const keyWa = bytesToWordArray(padBytes(key, AES_KEY_BYTES));
        const ivWa = bytesToWordArray(ivBytes);
        const cp = CipherParams.create({ ciphertext: bytesToWordArray(ctBytes), iv: ivWa });
        const decrypted = AES.decrypt(cp, keyWa, { iv: ivWa, mode: modeCtr });
        return wordArrayToBytes(decrypted);
    } catch (error) {
        console.error('AES-CTR decrypt bytes error:', error);
        return new Uint8Array(0);
    }
}

/**
 * AES-ECB 加密（字符串，无 IV，不推荐用于敏感数据）
 *
 * 注意：
 *   - 如果 key 的长度不足32位，则会在末尾补齐 字符'0'。
 */
export const aesEcbEncryptString = (data: string, key: string): string => {
    if (!data || !key) return '';
    const paddedKey = key.padEnd(32, '0').slice(0, 32);
    const keyHex = encUtf8.parse(paddedKey);
    const encrypted = AES.encrypt(data, keyHex, { mode: modeEcb });
    return encBase64.stringify(encrypted.ciphertext);
};

/**
 * AES-ECB 解密（字符串）
 */
export const aesEcbDecryptString = (encryptedData: string, key: string): string => {
    if (!encryptedData || !key) return '';
    try {
        const paddedKey = key.padEnd(32, '0').slice(0, 32);
        const keyHex = encUtf8.parse(paddedKey);
        const cp = CipherParams.create({ ciphertext: encBase64.parse(encryptedData) });
        const decrypted = AES.decrypt(cp, keyHex, { mode: modeEcb });
        return decrypted.toString(encUtf8);
    } catch (error) {
        console.error('AES-ECB decrypt error:', error);
        return '';
    }
};

/**
 * AES-ECB 加密（二进制，无 IV）
 *
 * 注意：
 *   - 如果 key 的长度不足 32 位，则会在末尾补齐 数字0。
 */
export function aesEcbEncryptBytes(data: Uint8Array, key: Uint8Array): Uint8Array {
    if (!data.length || !key.length) return new Uint8Array(0);
    const keyWa = bytesToWordArray(padBytes(key, AES_KEY_BYTES));
    const encrypted = AES.encrypt(bytesToWordArray(data), keyWa, { mode: modeEcb });
    return wordArrayToBytes(encrypted.ciphertext);
}

/**
 * AES-ECB 解密（二进制）
 */
export function aesEcbDecryptBytes(encryptedData: Uint8Array, key: Uint8Array): Uint8Array {
    if (!encryptedData.length || !key.length) return new Uint8Array(0);
    try {
        const keyWa = bytesToWordArray(padBytes(key, AES_KEY_BYTES));
        const cp = CipherParams.create({ ciphertext: bytesToWordArray(encryptedData) });
        const decrypted = AES.decrypt(cp, keyWa, { mode: modeEcb });
        return wordArrayToBytes(decrypted);
    } catch (error) {
        console.error('AES-ECB decrypt bytes error:', error);
        return new Uint8Array(0);
    }
}

/**
 * Rabbit 加密（字符串）
 *
 * 注意：
 *   - 如果如果 iv 为空，则返回的加密结果的头部会包含 iv，否则不包含 iv。
 *   - 如果 key 的长度不足 16 位，则会在末尾补齐 字符'0'。
 *   - 如果 iv 的长度不足 8 位，则会在末尾补齐 字符'0'。
 */
export const rabbitEncryptString = (data: string, key: string, iv?: string): string => {
    if (!data || !key) return '';
    const paddedKey = key.padEnd(16, '0').slice(0, 16);
    const ivValue = iv ? iv.padEnd(8, '0').slice(0, 8) : generateKeyString(8);
    const keyHex = encUtf8.parse(paddedKey);
    const ivHex = encUtf8.parse(ivValue);
    const encrypted = Rabbit.encrypt(data, keyHex, { iv: ivHex });
    const ciphertextBase64 = encBase64.stringify(encrypted.ciphertext);
    return iv ? ciphertextBase64 : ivValue + ciphertextBase64;
};

/**
 * Rabbit 解密（字符串）对应 rabbitEncryptString
 */
export const rabbitDecryptString = (encryptedData: string, key: string, iv?: string): string => {
    if (!encryptedData || !key) return '';
    try {
        const paddedKey = key.padEnd(16, '0').slice(0, 16);
        const ivValue = iv ? iv.padEnd(8, '0').slice(0, 8) : encryptedData.slice(0, 8);
        const ciphertextBase64 = iv ? encryptedData : encryptedData.slice(8);
        const keyHex = encUtf8.parse(paddedKey);
        const ivHex = encUtf8.parse(ivValue);
        const cp = CipherParams.create({ ciphertext: encBase64.parse(ciphertextBase64) });
        const decrypted = Rabbit.decrypt(cp, keyHex, { iv: ivHex });
        return decrypted.toString(encUtf8);
    } catch (error) {
        console.error('Rabbit decrypt error:', error);
        return '';
    }
};

/**
 * Rabbit 加密（二进制）
 *
 * 注意：
 *   - 如果如果 iv 为空，则返回的加密结果的头部会包含 iv，否则不包含 iv。
 *   - 如果 key 的长度不足 16 位，则会在末尾补齐 数字0。
 *   - 如果 iv 的长度不足 8 位，则会在末尾补齐 数字0。
 */
export function rabbitEncryptBytes(data: Uint8Array, key: Uint8Array, iv?: Uint8Array): Uint8Array {
    if (!data.length || !key.length) return new Uint8Array(0);
    const keyWa = bytesToWordArray(padBytes(key, RABBIT_KEY_BYTES));
    const ivWa = iv
        ? bytesToWordArray(padBytes(iv, RABBIT_IV_BYTES))
        : bytesToWordArray(generateKeyBytes(RABBIT_IV_BYTES));
    const encrypted = Rabbit.encrypt(bytesToWordArray(data), keyWa, { iv: ivWa });
    const ct = wordArrayToBytes(encrypted.ciphertext);
    if (iv) return ct;
    const out = new Uint8Array(RABBIT_IV_BYTES + ct.length);
    out.set(wordArrayToBytes(ivWa), 0);
    out.set(ct, RABBIT_IV_BYTES);
    return out;
}

/**
 * Rabbit 解密（二进制）
 */
export function rabbitDecryptBytes(
    encryptedData: Uint8Array,
    key: Uint8Array,
    iv?: Uint8Array
): Uint8Array {
    if (!encryptedData.length || !key.length) return new Uint8Array(0);
    try {
        const ivBytes = iv
            ? padBytes(iv, RABBIT_IV_BYTES)
            : encryptedData.subarray(0, RABBIT_IV_BYTES);
        const ctBytes = iv ? encryptedData : encryptedData.subarray(RABBIT_IV_BYTES);
        const keyWa = bytesToWordArray(padBytes(key, RABBIT_KEY_BYTES));
        const ivWa = bytesToWordArray(ivBytes);
        const cp = CipherParams.create({ ciphertext: bytesToWordArray(ctBytes), iv: ivWa });
        const decrypted = Rabbit.decrypt(cp, keyWa, { iv: ivWa });
        return wordArrayToBytes(decrypted);
    } catch (error) {
        console.error('Rabbit decrypt bytes error:', error);
        return new Uint8Array(0);
    }
}
