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

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";
import { Container } from "typescript-ioc";
import { NonStandardApiConfig } from "./Config";

let config: NonStandardApiConfig = Container.getValue('config.nonStandardApi');

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

        let obj = JSON.parse(decrypted);
        if (typeof data === 'object' && data['imageInfo']) {
            obj['imageInfo'] = data['imageInfo'];
        }

        return obj;
    }

    /**
     * 加密
     */
    static encrypt(data: object | string, key = config.key, iv = config.iv): { data: string, imageInfo?: string } {
        let dataWrap: any = {};
        if (data['imageInfo']) {
            dataWrap['imageInfo'] = data['imageInfo'];
        }

        let cipher = crypto.createCipheriv('des-ede3-cbc', key, iv);
        let encrypted = Buffer.concat([cipher.update(Buffer.from(typeof data === 'string' ? data : JSON.stringify(data))), cipher.final()]);

        dataWrap['data'] = encrypted.toString('base64');

        return dataWrap;
    }

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

/**
 * 非标接口装饰器
 * 
 * 自动对请求参数进行解密
 * 
 * 自动捕获异常并返回错误报文
 * 
 * 自动记录日志，记录请求参数及返回报文
 * 
 * 支持ioc配置config.nonStandardApi，可以重写加解密key、iv，可以配置secure是否启用加解密，如果secure配置为false则禁用加解密，未配置或其他情况则为启用
 * 
 * @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 secure_disabled = Contants.IS_LOCAL_DEBUG ||
                    //如果非标配置secure === false禁用加解密
                    config?.secure === false ||
                    //测试环境，如果参数带有secure_disabled === false禁用加解密，方便测试人员进行测试
                    (Contants.IS_TEST && args[0].secure_disabled === false);

                if (!secure_disabled) {
                    args[0] = Des3Utils.decrypt(args[0], config.key, config.iv);
                }

                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,
    des3: {
        key: string;
        iv: string;
    }
}) {
    let _config = config;

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

    axiosInstance.interceptors.request.use(config => {
        if (config.data) config.data = Des3Utils.encrypt(config.data, _config.des3.key, _config.des3.iv);
        return config;
    }, error => {
        throwApiErrorFromAxiosError(error, config.apiCategory);
    });

    axiosInstance.interceptors.response.use(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;
    }, error => {
        throwApiErrorFromAxiosError(error, config.apiCategory);
    });

    return axiosInstance;
}