import { logger } from "mslib";
import { IocConfigProvider, MemberConfig } from './Config';
import { ApiResponse } from './ApiResponse';
import { CryptoUtils } from './Security'; // 需包含DES解密方法
let memberapi_config: MemberConfig = IocConfigProvider.getMemberConfig();

/**
 * 3DS Token校验装饰器（针对prepay_id的校验）
 * 
 * 从请求参数中提取prepay_id，解密后验证：
 * 1. prepay_id是否存在
 * 2. 是否在有效期内（基于加密时的timestamp和expiresIn）
 * 3. 请求参数中的关键字段与加密时的原始数据是否一致
 * 
 * @param description 服务描述（日志用）
 * @param config 配置选项
 * @param config.isLog 是否记录日志，默认true
 * @param config.isVerify 是否启用校验，默认true
 */
export function Verify3DS(
    description?: string,
    config?: {
        isLog?: boolean;
        isVerify?: boolean;
        isEncrypt?: boolean,
        expiresIn?: number
    }
): MethodDecorator {
    // 默认配置
    const defaultConfig = {
        isLog: true,
        isVerify: true,
        isEncrypt: true,
        expiresIn: 300,//5分钟
        ...config
    };

    return function (target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
        // 装饰器初始化日志
        const tapdId = IocConfigProvider.getBasicConfig()?.tapdId || '';
        logger.info(
            `${tapdId}.${target.constructor.name}/1.0`,
            `Token校验装饰器 - ${description || '未提供描述'}`
        );

        // 保存原始方法
        const originalMethod = descriptor.value;

        // 重写方法（核心校验逻辑）
        descriptor.value = async function (...args: any[]) {
            const tapdId = IocConfigProvider.getBasicConfig()?.tapdId || '';
            const serviceName = description || `${target.constructor.name}.${propertyKey}`;

            // 1. 基础配置校验
            if (defaultConfig.isVerify) {
                if (!memberapi_config.key) {
                    const errorMsg = 'Token校验失败：未配置DES密钥（key）';
                    logger.error(`tapd:${tapdId} ${serviceName}`, errorMsg);
                    return ApiResponse.successResult(1, errorMsg, {});
                }
                if (!memberapi_config.iv) {
                    const errorMsg = 'Token校验失败：未配置DES向量（iv）';
                    logger.error(`tapd:${tapdId} ${serviceName}`, errorMsg);
                    return ApiResponse.successResult(1, errorMsg, {});
                }
            }

            // 2. 提取请求参数中的prepay_id（核心修改点）
            const requestParams = args[0] || {};
            const prepayId = requestParams.ExtParam?.prepay_id;  // 从prepay_id获取值
            const serviceInfo = `tapd:${tapdId} Token校验服务 ${serviceName}`;

            // 3. 记录请求日志
            if (defaultConfig.isLog) {
                logger.info(
                    `${serviceInfo} 请求参数:${JSON.stringify(requestParams)}，待校验prepay_id:${prepayId ? '存在' : '不存在'}`
                );
            }

            // 4. 校验开关：若不启用校验，直接执行原始方法
            if (!defaultConfig.isVerify) {
                return originalMethod.apply(this, args);
            }

            // 5. 校验prepay_id是否存在（核心修改点）
            if (!prepayId) {
                const errorMsg = '请求中未包含prepay_id，请检查ExtParam.prepay_id字段';
                logger.error(`${serviceInfo} 校验异常`, errorMsg);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }

            // 6. DES解密prepay_id（核心修改点）
            const decryptedStr = CryptoUtils.desDecrypt(prepayId, memberapi_config.key, memberapi_config.iv);
            if (!decryptedStr) {
                const errorMsg = 'prepay_id解密失败，可能是无效值或密钥不匹配';
                logger.error(`${serviceInfo} 校验异常`, errorMsg);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }

            // 7. 解析解密后的数据
            let decryptedData: {
                payload: any; // 加密时的原始结果
                timestamp: number; // 加密时的时间戳（秒级）
                expiresIn: number; // 过期时间（秒）
            };
            try {
                decryptedData = JSON.parse(decryptedStr);
            } catch (parseError) {
                const errorMsg = 'prepay_id解析失败，格式不正确';
                logger.error(`${serviceInfo} 校验异常`, errorMsg, parseError);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }

            // 8. 校验时效性
            const currentTimestamp = Math.floor(Date.now() / 1000); // 当前秒级时间戳
            const expireTimestamp = decryptedData.timestamp + decryptedData.expiresIn;
            if (currentTimestamp > expireTimestamp) {
                const errorMsg = `prepay_id已过期（有效期${decryptedData.expiresIn}秒），请重新获取`;
                logger.error(`${serviceInfo} 校验异常`, errorMsg);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }

            // 9. 校验通过，执行原始方法
            if (defaultConfig.isLog) {
                logger.info(`${serviceInfo} prepay_id校验通过，开始执行原始方法`);
            }

            // 处理原始方法执行及加密
            let result;
            try {
                result = await originalMethod.apply(this, args);
            } catch (methodError) {
                const errorMsg = methodError instanceof Error ? methodError.message : `原始方法执行失败：${String(methodError)}`;
                logger.error(`${serviceInfo} 原始方法执行异常`, errorMsg);
                return ApiResponse.successResult(1, errorMsg, {});
            }
            return result;
        };

        return descriptor;
    };
}

