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校验装饰器（DES解密+时效性+参数一致性校验）
 * 
 * 从请求参数中提取token，解密后验证：
 * 1. token是否在有效期内（基于加密时的timestamp和expiresIn）
 * 2. 请求参数中的关键字段与加密时的原始数据是否一致
 * 
 * @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. 提取请求参数中的token
            const requestParams = args[0] || {}; // 假设请求参数是第一个入参（根据实际场景调整）
            const token = requestParams.ExtParam?.token;
            const serviceInfo = `tapd:${tapdId} Token校验服务 ${serviceName}`;

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

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

            try {
                // 5. 校验token是否存在
                if (!token) {
                    throw new Error('请求中未包含token，请检查ExtParam.token字段');
                }

                // 6. DES解密token（反向解析加密时的数据）
                const decryptedStr = CryptoUtils.desDecrypt(token, memberapi_config.key, memberapi_config.iv);
                if (!decryptedStr) {
                    throw new Error('token解密失败，可能是无效token或密钥不匹配');
                }

                // 7. 解析解密后的数据（与加密时的dataToEncrypt结构对应）
                const decryptedData: {
                    payload: any; // 加密时的原始结果（即第一个响应中的data）
                    timestamp: number; // 加密时的时间戳（秒级）
                    expiresIn: number; // 过期时间（秒）
                } = JSON.parse(decryptedStr);

                // 8. 校验时效性（当前时间是否在有效期内）
                const currentTimestamp = Math.floor(Date.now() / 1000); // 当前秒级时间戳
                const expireTimestamp = decryptedData.timestamp + decryptedData.expiresIn;
                if (currentTimestamp > expireTimestamp) {
                    throw new Error(`token已过期（有效期${decryptedData.expiresIn}秒），请重新获取`);
                }

                // 9. 校验参数一致性（对比请求参数与加密时的原始数据）


                // 参数一致性校验（按validateParams逻辑实现）
                const validationResult = validateParams(requestParams, decryptedData.payload.data);
                if (!validationResult.isValid) {
                    throw new Error(`参数不一致：${validationResult.errors.join('; ')}`);
                }

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

                // 把当前结果加密到prepay_id里
                let result;

                // 调用原始方法获取结果
                result = await originalMethod.apply(this, args);

                // 如果需要加密且结果有效，则进行DES加密处理
                if (result && typeof result === 'object') {
                    // 确保有DES密钥
                    if (!memberapi_config.key) {
                        throw new Error('DES加密失败：未配置key');
                    }
                    // 确保有DES密钥
                    if (!memberapi_config.iv) {
                        throw new Error('DES加密失败：未配置iv');
                    }

                    // 生成当前时间戳（秒级）
                    const timestamp = Math.floor(Date.now() / 1000);

                    // 构建包含原始数据和时间戳的加密对象
                    const dataToEncrypt = {
                        payload: result,
                        timestamp,
                        expiresIn: defaultConfig.expiresIn
                    };

                    // 使用DES加密
                    const token = CryptoUtils.desEncrypt(JSON.stringify(dataToEncrypt), memberapi_config.key, memberapi_config.iv);

                    // 确保ExtParam对象存在
                    if (!result.ExtParam) {
                        result.ExtParam = {};
                    }

                    // 将加密结果存入ExtParam.token字段
                    result.ExtParam.token = token;
                    return result
                }
            } catch (error) {
                // 11. 捕获校验异常，返回错误响应
                const errorMsg = error instanceof Error ? error.message : `校验失败：${String(error)}`;
                logger.error(`${serviceInfo} 校验异常`, errorMsg);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }
        };

        return descriptor;
    };
}

/**
 * 校验前端参数与原始参数的一致性
 * 按照提供的validateParams方法逻辑实现
 * @param frontData 前端参数对象
 * @param rawData 原始参数中的data对象
 * @returns 校验结果：{isValid: 布尔值, errors: 错误信息数组}
 */
function validateParams(frontData: any, rawData: any) {
    const errors: string[] = [];
    const front = frontData || {}; // 前端参数
    const raw = rawData || {}; // 原始数据中的data部分

    // 1. 校验车牌号（PlateNo）
    const frontPlateNo = front.PlateNo;
    const rawPlateNo = raw.CarplateData?.PlateNo;
    if (frontPlateNo !== rawPlateNo) {
        errors.push(`车牌号不一致：前端="${frontPlateNo}"，原始="${rawPlateNo}"`);
    }

    // 2. 校验VIP等级（VIPLevel）
    const frontVIPLevel = front.VIPLevel;
    const rawVIPLevel = raw.VIPData?.VIPLevel;
    if (frontVIPLevel !== rawVIPLevel) {
        errors.push(`VIP等级不一致：前端="${frontVIPLevel}"，原始="${rawVIPLevel}"`);
    }

    // 3. 校验token（令牌一致性）
    const frontToken = front.ExtParam?.token;
    const rawToken = raw.ExtParam?.token;
    if (frontToken !== rawToken) {
        // 长字符串截断显示，便于调试
        const truncate = (str?: string) => str?.length > 20 ? str.slice(0, 20) + '...' : str;
        errors.push(`token不一致：前端="${truncate(frontToken)}"，原始="${truncate(rawToken)}"`);
    }

    // 4. 校验优惠券数组（CouponData）
    const frontCoupons = front.CouponData || [];
    const rawCoupons = raw.CouponData || [];

    // 4.1 数组长度一致性
    if (frontCoupons.length !== rawCoupons.length) {
        errors.push(`优惠券数量不一致：前端=${frontCoupons.length}条，原始=${rawCoupons.length}条`);
    } else {
        // 4.2 逐条校验优惠券字段
        frontCoupons.forEach((frontCoupon: any, index: number) => {
            const rawCoupon = rawCoupons[index];
            if (!rawCoupon) {
                errors.push(`优惠券[${index}]：原始参数缺失`);
                return;
            }

            // 规则ID
            if (frontCoupon.RuleId !== rawCoupon.RuleId) {
                errors.push(`优惠券[${index}]规则ID不一致：前端=${frontCoupon.RuleId}，原始=${rawCoupon.RuleId}`);
            }

            // 规则名称
            if (frontCoupon.RuleName !== rawCoupon.RuleName) {
                errors.push(`优惠券[${index}]名称不一致：前端="${frontCoupon.RuleName}"，原始="${rawCoupon.RuleName}"`);
            }

            // 优惠券编号
            if (frontCoupon.TicketNo !== rawCoupon.TicketNo) {
                errors.push(`优惠券[${index}]编号不一致：前端="${frontCoupon.TicketNo}"，原始="${rawCoupon.TicketNo}"`);
            }

            // 面额（前端TicketValue对应原始DisAmout）
            if (frontCoupon.DisAmout !== rawCoupon.TicketValue) {
                errors.push(`优惠券[${index}]面额不一致：前端=${frontCoupon.DisAmout}，原始=${rawCoupon.TicketValue}`);
            }

            // 扩展参数
            if (frontCoupon.ExtParam !== rawCoupon.ExtParam) {
                errors.push(`优惠券[${index}]扩展参数不一致：前端=${frontCoupon.ExtParam}，原始=${rawCoupon.ExtParam}`);
            }
        });
    }

    return {
        isValid: errors.length === 0,
        errors: errors
    };
}