/**
 * 非标接口模块
 */

import { Context, logger } from "mslib"
import { ApiResponse } from "./TyApiCommon"
import Contants, { IS_PROD, IS_TEST, IS_DEBUG } from "./Contants";
import { ApiError, FriendlyError, ParamEmptyError } from "./Errors";
import { IocConfigProvider, NonStandardApiConfig } from "./Config";
import { CryptoUtils } from "./Security";
import axios from 'axios';
const CryptoJS = require('crypto-js');

export let defaults = {
    key: 'Keytop:1234567812345678!',
    iv: '20210723'
}

let nonstandard_api_config: NonStandardApiConfig = IocConfigProvider.getNonStandardApiConfig();

/**
 * 非标接口3des加解密工具
 */
export class Des3Utils {
    /**
     * 加密
     */
    static encrypt(data: object | string, key: string, iv: string): { data: string, imageInfo: any } {
        let dataWrap: any = {};
        let _key = CryptoJS.enc.Utf8.parse(key);
        let cfg: any = {
            mode: 'CBC',
            padding: 'Pkcs7',
            iv: CryptoJS.enc.Utf8.parse(iv)
        }

        if (typeof data === 'string') {
            dataWrap.data = CryptoUtils.des3Encrypt(data, _key, cfg).toString();
        }
        else {
            dataWrap.data = CryptoUtils.des3Encrypt(JSON.stringify(data, (k, v) => {
                if (k === 'imageInfo') return;
                return v;
            }), _key, cfg).toString();

            if (data['imageInfo']) dataWrap.imageInfo = data['imageInfo'];
        }

        return dataWrap;
    }

    /**
     * 解密
     */
    static decrypt(data: string | { data: string }, key: string, iv: string): object {
        let _key = CryptoJS.enc.Utf8.parse(key);
        let cfg: any = {
            mode: 'CBC',
            padding: 'Pkcs7',
            iv: CryptoJS.enc.Utf8.parse(iv)
        }

        if (typeof data === 'string') {
            return JSON.parse(CryptoUtils.des3Decrypt(data, _key, cfg));
        } else {
            let obj = JSON.parse(CryptoUtils.des3Decrypt(data.data, _key, cfg));
            delete data.data;
            Object.assign(data, obj);
            return data;
        }
    }
}

/**
 * 非标接口装饰器
 * 
 * 自动对请求参数进行解密(`DEBUG`环境下禁用), 异常捕获, 记录日志, 自动返回失败报文`ApiResponse.failed('xxx')`
 * 
 * ```ts
 * import { ApiResponse, NonStandardApi } from "refs/nslib";
 * 
 * export class RemoteOpen implements IService {
 *    //去掉注释
 *    //@NonStandardApi.Service('远程开闸')
 *    async doService(input) : Promise<ApiResponse> {
 * 
 *       return ApiResponse.success;
 *    }
 * }
 * ```
 * @param description 服务描述，仅为日志输出
 */
export function Service(description?: string, config?: {
    /**
     * 是否记录接口调用(成功)日志
     */
    isLog?: boolean,

    /**
     * 是否启用加解密
     */
    secure?:boolean
}): MethodDecorator {
    return function (target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
        logger.info(`${Contants.SS_SERVICE_BASE_URL}/${Contants.P_ID}.${target.constructor.name}/1.0`, description);

        let method = descriptor.value;

        descriptor.value = async function (...args) {
            logger.info(`非标接口 ${description || target.constructor.name} 请求参数:${JSON.stringify(args)}`);

            let response;
            
            try {
                /**
                 * 是否启用加解密
                 * 
                 * 1.非标api可配置是否启用加解密;
                 * 
                 * 2.接口装饰器可配置是否启用加解密;
                 * 
                 * 3.调试环境默认禁用
                 * 
                 * 4.调试情况下，测试人员可禁用加解密
                 */
                
                let is_secure = !(nonstandard_api_config?.secure === false) &&
                    !(config?.secure === false) &&
                    !IS_DEBUG &&
                    !(IS_TEST && args[0]?.secure === false);

                if(IS_DEBUG && config?.secure === true){
                    is_secure = true
                }

                if (is_secure) {
                    if(args[0].data){
                        args[0] = Des3Utils.decrypt(args[0].data, nonstandard_api_config?.key || defaults.key, nonstandard_api_config?.iv || defaults.iv);
                    }else{
                        args[0] = Des3Utils.decrypt(args[0], nonstandard_api_config?.key || defaults.key, nonstandard_api_config?.iv || defaults.iv);
                    }
                    logger.info(`非标接口 ${description || target.constructor.name} 请求参数(明文):${JSON.stringify(args, jsonLongStrReplacer)}`);
                }

                response = await method.apply(this, args);
            } catch (error) {
                logger.error(`非标接口 ${description || target.constructor.name} 出现异常`, error);
                if(error instanceof FriendlyError || error instanceof ParamEmptyError) response = ApiResponse.failed(error.message);
                else response = IS_PROD ? ApiResponse.failed('服务器异常') : ApiResponse.failed(error.toString());
            }
            
            if (!(config?.isLog === false)) logger.info(`非标接口 ${description || target.constructor.name } 返回值:${JSON.stringify(response)}`);

            return response;
        }
    }
}

function jsonLongStrReplacer(key: string, value: any) {
    //过滤长字符串
    if (typeof value === 'string' && value.length > 500) {
        return `${value.substring(0, 500)}...`;
    }

    return value;
}

/**
 * @returns Context.getRequestHeaders()['kt-token']
 */
export function getTokenFromHeader() {
    const headers = Context.getRequestHeaders();
    return headers['kt-token'];
}