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

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

/**
 * 默认配置
 */
const defaults = {
    des3: {
        key: 'Keytop:1234567812345678!',
        iv: '20210723'
    }
}

/**
 * 非标接口配置
 */
export const config = Object.assign(defaults, getNonStandardApiConfig());

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

/**
 * 非标接口加解密工具
 */
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');
    }
}

/**
 * 非标接口装饰器
 * 
 * 自动解密，异常捕获，日志记录，发生异常自动返回异常报文等
 * @param description 服务描述
 * @returns
 */
export function Service(description?: string): MethodDecorator {
    return function (target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
        logger.info(`${Contants.SS_SERVICE_BASE_URL}${process.env["KT_PID"]}.${target.constructor.name}/1.0`, description);

        let method = descriptor.value;

        descriptor.value = async function (...args) {
            let response;
            let _error;

            try {
                if (!Contants.IS_DEBUG && args) {
                    decrypt(args[0], config.des3.key, config.des3.iv);
                }

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

                _error = error;
            }

            logger[_error ? 'error' : 'info'](`非标接口'${target.constructor.name}'执行${_error ? '失败' : '成功'},request:${JSON.stringify(args, logRequestJSONReplacer)},response:${JSON.stringify(response)}`, _error);

            return response;
        }
    }
}

/**
 * request 日志记录 JSON replacer
 * 
 * 过滤前端传过来的base64图片字符串
 * @param key 
 * @param value 
 * @returns 
 */
function logRequestJSONReplacer(key, value) {
    if (key === 'imageInfo' || (typeof value === 'string' && value.startsWith('data:'))) return 'base64Str';

    return value;
}

/**
 * 生成遵循非标加解密规则的axios实例
 * 
 * 第三方按照非标的接口文档开发接口，那么非标对接的时候可以使用该方法生成客户端axios实例
 */
export function generateAxiosInstance(axioConfig: {
    baseUrl: string,
    timeout?: number,
    axiosErrorHandler?: (error: any) => any,
    axiosResponseInterceptHandler?: (response: AxiosResponse<any>) => any
}, des3Config: {
    key: string;
    iv: string;
}) {
    let defaults = {
        axiosErrorHandler: error => {
            if (error.isAxiosError) throw generateApiErrorFromAxiosError(error);
            throw error;
        },
        axiosResponseInterceptHandler: response => {
            logger.info(`第三方API'${response.config.url}'调用成功,requestUrl:${response.config.baseURL + response.config.url},headers:${JSON.stringify(response.config.headers)},requestData:${response.config.data},response:${JSON.stringify(response.data)}`);
            return response;
        }
    }

    axioConfig = Object.assign(defaults, axioConfig);

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

    axiosInstance.interceptors.request.use(config => {
        config.data = encrypt(config.data, des3Config.key, des3Config.iv);
        return config;
    }, error => {
        return axioConfig.axiosErrorHandler(error);
    });

    axiosInstance.interceptors.response.use(response => {
        return axioConfig.axiosResponseInterceptHandler(response) || response;
    }, error => {
        return axioConfig.axiosErrorHandler(error);
    });

    return axiosInstance;
}

/**
 * 非标接口对请求参数加密
 */
export function encrypt(data, key, iv) {
    if (!data || typeof data !== 'object') return data;
    if (!Reflect.ownKeys(data).find(s => s !== imageInfo)) return data;

    let imageInfo = null;
    if (data.imageInfo) {
        imageInfo = data.imageInfo;
        delete data.imageInfo;
    }

    let dataWrap = {
        data: Des3Utils.encrypt(JSON.stringify(data), key, iv),
        imageInfo: imageInfo
    };

    return dataWrap;
}

/**
 * 非标接口对请求参数解密
 */
export function decrypt(data, key, iv) {
    if (!data || typeof data !== 'object' || !data.data || typeof data.data !== 'string') return;

    let decrypted;

    try {
        decrypted = Des3Utils.decrypt(data.data, key, iv);
    }
    catch (error) {
        throw new ParamDecryptFailedError();
    }

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

/**
 * 随机生成非标接口3DES加解密24位key
 */
export async function generateKey() {
    return crypto.getDiffieHellman('modp14').generateKeys('base64').substring(0, 24);
}