import isNil from 'ramda/es/isNil';

import globalConfig from '@/config';
import { CompressMethod } from '@/config/types';
// import { bytesBase64decode } from '@/utils/crypto';
import { bytesToString, stringToBytes } from '@/utils/bytesUtils';
import { bytesBase64encode } from '@/utils/crypto';
import {
    IRequestInterceptorAxios,
    IResponseInterceptor,
    RequestConfig,
} from '@/utils/request/types';
import { decryptResponsePayload, encryptRequestPayload } from '@/utils/requestCrypto';
import { currentUnixTimestamp } from '@/utils/timeUtils';

/**
 * 请求数据加密拦截器
 * 将请求体加密为二进制（可选压缩 + 时间戳 + AES-CBC），以二进制发送
 */
export const requestEncryptionInterceptor: IRequestInterceptorAxios = async (config) => {
    const {
        enabled: optEnableEncryption,
        key: optEncryptionKey,
        compressMethod: optCompressMethod,
    } = (config as RequestConfig)?.encryption ?? {};

    const enableEncryption = optEnableEncryption ?? globalConfig.security.enableRequestEncryption;
    const encryptionKey = optEncryptionKey ?? globalConfig.security.requestEncryptionKey;
    const compressMethod = optCompressMethod ?? globalConfig.security.compressMethod;

    if (!enableEncryption) {
        return config;
    }

    // 指定 axios 按二进制处理响应数据
    config.responseType = 'arraybuffer';

    // 指定接口数据压缩方式
    config.headers = config.headers ?? {};
    config.headers['X-NL-Content-Encoding'] = compressMethod;

    // 设置 X-Request-Timestamp
    const existingTs = config.headers['X-Request-Timestamp'];
    let timestamp: number;
    if (!isNil(existingTs) && existingTs !== '') {
        timestamp = Number(existingTs);
        if (Number.isNaN(timestamp)) timestamp = currentUnixTimestamp();
    } else {
        timestamp = currentUnixTimestamp();
        config.headers['X-Request-Timestamp'] = timestamp;
    }

    config.data = config.data ?? {}; // 如果请求体为空，则设置为空对象（后端需要非空请求体）
    console.log(
        `⬆️ 🔐 [requestEncryptionInterceptor] api (${config.url}) request data:`,
        config.data
    );
    if (['post', 'put'].includes(config.method?.toLowerCase() || '')) {
        // 设置 Content-Type 为 application/octet-stream
        config.headers['Content-Type'] = 'application/octet-stream';

        // 加密请求体
        // const keyBytes = bytesBase64decode(encryptionKey);
        const keyBytes = stringToBytes(encryptionKey);
        const dataBytes = new TextEncoder().encode(JSON.stringify(config.data));
        const encrypted = await encryptRequestPayload(
            dataBytes,
            timestamp,
            keyBytes,
            compressMethod as CompressMethod
        );

        console.log(
            `⬆️ 🔐 [requestEncryptionInterceptor] api (${config.url}) request data encrypted,`,
            `timestamp: ${timestamp},`,
            `data: ${bytesBase64encode(encrypted)}`
        );

        // 设置请求体
        config.data = encrypted.buffer;
    }

    return config;
};

/**
 * 响应数据解密拦截器
 * 将服务端返回的二进制密文解密（AES-CBC → 去时间戳 → 解压）并解析为 JSON
 */
export const responseDecryptionInterceptor: IResponseInterceptor = async (response) => {
    const {
        enabled: optEnableEncryption,
        key: optEncryptionKey,
        compressMethod: optCompressMethod,
    } = (response.config as RequestConfig)?.encryption ?? {};

    const enableEncryption = optEnableEncryption ?? globalConfig.security.enableRequestEncryption;
    const encryptionKey = optEncryptionKey ?? globalConfig.security.requestEncryptionKey;
    const compressMethod = optCompressMethod ?? globalConfig.security.compressMethod;

    if (!enableEncryption) {
        return response;
    }

    // 后端永远加密响应体，但不设置 X-Response-Encrypted，因此不用判断 header 中的 X-Response-Encrypted 字段
    // const isEncrypted = response.headers?.['X-Response-Encrypted'] === 'true';
    const isEncrypted = true;
    if (isEncrypted && response.data) {
        const raw = response.data as ArrayBuffer | unknown;
        const encryptedBytes = new Uint8Array(
            raw instanceof ArrayBuffer ? raw : (raw as ArrayBuffer)
        );
        // const keyBytes = bytesBase64decode(encryptionKey);
        const keyBytes = stringToBytes(encryptionKey);
        const decrypted = await decryptResponsePayload(
            encryptedBytes,
            keyBytes,
            compressMethod as CompressMethod
        );
        console.log(
            `⬇️ 🔓 [responseDecryptionInterceptor] api (${response.config.url}) response data decrypted,`,
            `timestamp: ${decrypted?.timestamp},`,
            `data: ${bytesToString(decrypted?.data)}`
        );
        if (decrypted?.data?.length) {
            try {
                const dataStr = bytesToString(decrypted.data);
                response.data = JSON.parse(dataStr);
                console.log(
                    `⬇️ [responseDecryptionInterceptor] api (${response.config.url}) response data parsed:`,
                    response.data
                );
            } catch (error) {
                console.error(
                    `⬇️ [responseDecryptionInterceptor] api (${response.config.url}) response data parse failed:`,
                    error
                );
            }
        }
    }
    return response;
};
