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

import { logger } from "mslib"
import { ApiResponse } from "./TyApiCommon"
import { ParamDecryptFailedError, ParamError } from "./Errors"
import Contants from "./Contants";
import * as crypto from 'crypto'
import Axios, { AxiosResponse } from "axios";
import { ApiError, throwApiErrorFromAxiosError } from "./Errors";

/**
 * 非标接口请求基类
 */
export class ApiRequestBase {
    parkId: any;
}

/**
 * 非标接口3des加解密工具
 */
export class Des3Utils {
    /**
     * 解密
     */
    static decrypt(data, key, iv) {
        const decipher = crypto.createDecipheriv('des-ede3-cbc', key, iv);
        let decrypted = decipher.update(data, 'base64', 'utf8');
        decrypted += decipher.final('utf8');

        return decrypted;
    }

    /**
     * 加密
     */
    static encrypt(data, key, iv) {
        const cipher = crypto.createCipheriv('des-ede3-cbc', key, iv);
        const encrypted = Buffer.concat([cipher.update(Buffer.from(data)), cipher.final()]);

        return encrypted.toString('base64');
    }
}

/**
 * 非标接口加解密工具
 */
export class SecurityUtils {
    /**
     * 对数据进行加密并返回加密对象(非标加解密)
     */
    static encrypt(data, key, iv) {
        let dataWrap = {};
        if (data.imageInfo) {
            dataWrap['imageInfo'] = data.imageInfo;
            delete data.imageInfo;
        }

        dataWrap['data'] = Des3Utils.encrypt(JSON.stringify(data), key, iv);

        return dataWrap;
    }

    /**
     * 对数据进行解密并返回解密对象(非标加解密)
     */
    static decrypt(data, key, iv) {
        let decrypted = Des3Utils.decrypt(data?.data, key, iv);

        //解密成功删除密文
        delete data.data;

        return Object.assign(data, JSON.parse(decrypted));
    }

    /**
     * 随机生成24位的字符串
     */
    static generateKey() {
        return crypto.getDiffieHellman('modp14').generateKeys('base64').substring(0, 24);
    }
}

/**
 * 非标接口装饰器
 * 
 * 自动对请求参数进行解密
 * 
 * 自动捕获异常并返回错误报文
 * 
 * 自动记录日志，记录请求参数及返回报文
 * @param description 服务描述，仅为日志输出，提高可读性
 */
export function Service(description?: string): 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) {
            let response;
            let _error;

            try {
                let validate_decrypt = true;

                if (!Contants.IS_DEBUG && args[0]?.data) {
                    try {
                        args[0] = SecurityUtils.decrypt(args[0], 'Keytop:1234567812345678!', '20210723');
                    } catch (error) {
                        validate_decrypt = false;
                        response = ApiResponse.failed('解密失败');

                        logger.error(error);
                    }
                }

                if (validate_decrypt) response = await method.apply(this, args);
            } catch (error) {
                if (!Contants.IS_PROD) response = ApiResponse.failed(error.toString());
                else {
                    if (error instanceof ParamError) response = ApiResponse.failed(error.message);
                    else response = ApiResponse.failed('服务器异常');
                }

                _error = error;
            }

            if (_error)
                logger.error(`${description || '非标接口'}${target.constructor.name}出现异常,请求参数:${JSON.stringify(args, replaceBase64Str)},返回值:${JSON.stringify(response)}`, `\n异常信息>>>>>`, _error instanceof ApiError && !Contants.IS_DEBUG ? _error.toString() : _error);
            else
                logger.info(`${description || '非标接口'}${target.constructor.name},请求参数:${JSON.stringify(args, replaceBase64Str)},返回值:${JSON.stringify(response)}`);

            return response;
        }
    }
}

function replaceBase64Str(key, value) {
    if (key === 'imageInfo' || (typeof value === 'string' && value.startsWith('data:'))) return '[base64Str]';
    return value;
}

/**
 * 生成非标axios实例
 */
export function generateAxiosInstance(config: {
    apiCategory: string
    baseUrl: string,
    timeout?: number,
    axiosErrorHandler?: (error: any) => any,
    axiosResponseHandler?: (response: AxiosResponse<any>) => AxiosResponse<any> | Promise<AxiosResponse<any>>,
    des3: {
        key: string;
        iv: string;
    }
}) {
    let defaults = {
        timeout: 10000,
        axiosErrorHandler: error => { throwApiErrorFromAxiosError(error, config.apiCategory); },
        axiosResponseHandler: response => {
            logger.info(`${config.apiCategory} ${response.config['apiDesc'] || response.config.url}调用成功,请求地址:${response.config.baseURL + response.config.url},请求头:${JSON.stringify(response.config.headers)},请求参数:${response.config.data},返回值:${JSON.stringify(response.data)}`);
            return response;
        }
    }

    let _config = Object.assign({}, defaults, config);

    const axiosInstance = Axios.create({
        baseURL: _config.baseUrl,
        timeout: _config.timeout || defaults.timeout,
    });

    axiosInstance.interceptors.request.use(config => {
        if (config.data) config.data = SecurityUtils.encrypt(config.data, _config.des3.key, _config.des3.iv);
        return config;
    }, error => {
        if (typeof _config.axiosErrorHandler === 'function') return _config.axiosErrorHandler(error);
        defaults.axiosErrorHandler(error);
    });

    axiosInstance.interceptors.response.use(response => {
        if (typeof _config.axiosResponseHandler === 'function') return _config.axiosResponseHandler(response) || response;
        return defaults.axiosResponseHandler(response);
    }, error => {
        if (typeof _config.axiosErrorHandler === 'function') return _config.axiosErrorHandler(error);
        defaults.axiosErrorHandler(error);
    });

    return axiosInstance;
}