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 PreReCalFeeDecorator(
    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
            // let key = "Keytop:1234567812345678!";
            // let iv = "20210723";
            let requestParams = args[0] || {}; // 假设请求参数是第一个入参（根据实际场景调整）
            // requestParams = CryptoUtils.desDecrypt(requestParams, key, iv)
            const token = requestParams?.jwtToken;
            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);
            }

            // 5. 校验token是否存在
            if (!token) {
                const errorMsg = '请求中未包含token，请检查jwtToken字段';
                logger.error(`${serviceInfo} 校验异常`, errorMsg);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }

            // 6. DES解密token（反向解析加密时的数据）
            const decryptedStr = CryptoUtils.desDecrypt(token, memberapi_config.key, memberapi_config.iv);
            if (!decryptedStr) {
                const errorMsg = 'token解密失败，可能是无效token或密钥不匹配';
                logger.error(`${serviceInfo} 校验异常`, errorMsg);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }

            // 7. 解析解密后的数据（与加密时的dataToEncrypt结构对应）
            let decryptedData: {
                payload: any; // 加密时的原始结果（即第一个响应中的data）
                timestamp: number; // 加密时的时间戳（秒级）
                expiresIn: number; // 过期时间（秒）
            };
            try {
                decryptedData = JSON.parse(decryptedStr);
            } catch (parseError) {
                const errorMsg = 'token解析失败，格式不正确';
                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 = `token已过期（有效期${decryptedData.expiresIn}秒），请重新获取`;
                logger.error(`${serviceInfo} 校验异常`, errorMsg);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }

            // 9. 校验参数一致性（对比请求参数与加密时的原始数据）
            const validationResult = validateParams(requestParams, decryptedData.payload.data);
            if (!validationResult.isValid) {
                const errorMsg = `参数不一致：${validationResult.errors.join('; ')}`;
                logger.error(`${serviceInfo} 校验异常`, errorMsg);
                return ApiResponse.successResult(1, `Token校验失败：${errorMsg}`, {});
            }

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

            // 把当前结果加密到prePayId里
            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, {});
            }

            // 如果需要加密且结果有效，则进行DES加密处理
            if (result && typeof result === 'object') {

                //非正常可抵扣数据不赋值prePayId
                if (result.resCode != 0) {
                    return result;
                }

                // 确保有DES密钥
                if (!memberapi_config.key) {
                    const errorMsg = 'DES加密失败：未配置key';
                    logger.error(`${serviceInfo} 加密异常`, errorMsg);
                    return ApiResponse.successResult(1, errorMsg, {});
                }
                // 确保有DES向量
                if (!memberapi_config.iv) {
                    const errorMsg = 'DES加密失败：未配置iv';
                    logger.error(`${serviceInfo} 加密异常`, errorMsg);
                    return ApiResponse.successResult(1, errorMsg, {});
                }
                // 生成当前时间戳（秒级）
                const timestamp = Math.floor(Date.now() / 1000);

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

                // 使用DES加密
                const encryptToken = CryptoUtils.desEncrypt(JSON.stringify(dataToEncrypt), memberapi_config.key, memberapi_config.iv);
                // 将加密结果存入prePayId字段
                result.data.prePayId = encryptToken;
                return result;
            }

            return result;
        };

        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}"`);
    }

    // 2.1 等级抵扣限制和额度校验
    const frontBenefitsData = front?.BenefitsData || {};
    const rawBenefitsData = raw?.BenefitsData || {};
    if (rawBenefitsData?.VIPBenefit) {
        if (!frontBenefitsData) {
            errors.push(`等级抵扣前端参数缺失`);
        }
        if (frontBenefitsData?.VIPBenefits != rawBenefitsData?.VIPBenefits) {
            errors.push(`等级抵扣额度不一致：前端=${frontBenefitsData?.VIPBenefits}，原始=${rawBenefitsData?.VIPBenefits}`);
        }

        if (frontBenefitsData?.BenefitsType != rawBenefitsData?.BenefitsType) {
            errors.push(`等级抵扣使用额度不一致：前端=${frontBenefitsData?.BenefitsType}，原始=${rawBenefitsData?.BenefitsType}`);
        }
    }


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

    // 3.1 数组长度一致性
    if (frontCoupons.length > rawCoupons.length) {
        errors.push(`优惠券数量不一致：前端=${frontCoupons.length}条，原始=${rawCoupons.length}条`);
    } else {
        // 3.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}`);
            }
            // 抵扣类型（前端DisType对应原始TicketType）
            if (frontCoupon.DisType !== rawCoupon.TicketType) {
                errors.push(`优惠券[${index}]抵扣类型不一致：前端=${frontCoupon.DisType}，原始=${rawCoupon.TicketType}`);
            }

        });
    }
    // 3.2 校验前端的卷是有无重复卷
    if (!checkUnique(frontCoupons, 'TicketNo')) {
        errors.push(`优惠券存在重复TicketNo`);
    }

    // 4.1 消费抵扣限制和额度校验
    const frontConsumeData = front.ConsumeData || {};
    const rawConsumeData = raw.ConsumeData || {};
    // 消费抵扣规则存在
    if (rawConsumeData?.DiscountRule) {
        if (!frontConsumeData) {
            errors.push(`消费抵扣前端参数缺失`);
        }
        if (frontConsumeData?.DisAmout != rawConsumeData?.DiscountRule[0]?.DiscountLimit) {
            errors.push(`消费抵扣额度不一致：前端=${frontConsumeData?.DisAmout}，原始=${rawConsumeData?.DiscountRule[0]?.DiscountLimit}`);
        }

        if (frontConsumeData?.Consume != rawConsumeData?.DiscountRule[0]?.ConsumeLimit) {
            errors.push(`消费抵扣使用额度不一致：前端=${frontConsumeData?.Consume}，原始=${rawConsumeData?.DiscountRule[0]?.ConsumeLimit}`);
        }
    }

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

/**
 * 检查对象数组中指定字段是否存在重复值
 * @param {Array} arr 要检查的对象数组
 * @param {string} key 要检查的字段名（如 'id'、'name'）
 * @returns {boolean} 存在重复值返回 false，无重复返回 true
 */
function checkUnique(arr, key) {
    // 边界处理：非数组或空数组直接返回 true（无重复）
    if (!Array.isArray(arr) || arr.length === 0) return true;

    const valueSet = new Set(); // 利用 Set 存储已出现的值，自动去重

    for (const item of arr) {
        // 跳过非对象元素（避免报错）
        if (typeof item !== 'object' || item === null) continue;

        const value = item[key];
        // 若当前值已在 Set 中存在，说明重复，返回 false
        if (valueSet.has(value)) {
            return false;
        }
        // 否则将值存入 Set
        valueSet.add(value);
    }

    // 遍历完所有元素无重复，返回 true
    return true;
}