import * as crypto from 'crypto';
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios";
import { SignUtil } from "./SignUtil";
import { ApiRequestBase, ApiResponse, GetConfigInfoResponse } from "./Dto";

type Option = {
    /**
     * 接口根地址
     */
    url: string,

    appId: string,
    appSecret: string,

    /**
     * 默认1.0.0
     */
    version?: string,

    /**
     * 默认zh-CN
     */
    lang?: string,

    /**
     * 默认10秒
     */
    timeout?: number,

    requestInterceptor?: { onFulfilled?: (value: InternalAxiosRequestConfig<any>) => InternalAxiosRequestConfig<any> | Promise<InternalAxiosRequestConfig<any>>, onRejected?: (error: any) => any },
    responseInterceptor?: { onFulfilled?: (value: AxiosResponse<any, any>) => AxiosResponse<any, any> | Promise<AxiosResponse<any, any>>, onRejected?: (error: any) => any }
}

function generateAxiosInstance(opt: Option) {
    let axiosInstance = axios.create({
        baseURL: opt.url,
        timeout: opt.timeout || 10000,
        headers: {
            'Content-Type': 'application/json;charset=utf-8',
            version: opt.version || '1.0.0',
            'accept-language': opt.lang || 'zh-CN'
        }
    });

    let default_req_interceptor = (config: InternalAxiosRequestConfig) => {
        let data = (config.data || {}) as ApiRequestBase;

        //填充serviceCode
        if (!data.serviceCode && config.url) {
            let arr = config.url.split('/');
            let serviceCode = arr[arr.length - 1];
            data.serviceCode = serviceCode.charAt(0).toLowerCase() + serviceCode.slice(1);
        }

        //签名
        if (!data.key) {
            data.appId = opt.appId;
            data.reqId = crypto.randomUUID().replace(/-/g, '');
            data.ts = Date.now().toString();
            data.key = SignUtil.sign(data, opt.appSecret);
        }

        config.data = data;

        return config;
    };

    //请求拦截器按添加顺序倒叙执行
    axiosInstance.interceptors.request.use(opt.requestInterceptor?.onFulfilled, opt.requestInterceptor?.onRejected);
    axiosInstance.interceptors.request.use(default_req_interceptor);
    axiosInstance.interceptors.response.use(opt.responseInterceptor?.onFulfilled, opt.responseInterceptor?.onRejected);

    return axiosInstance;
}

/**
 * 统一接口客户端
 */
export class ApiClient {
    public readonly axiosInstance: AxiosInstance;

    constructor(opts: Option) {
        this.axiosInstance = generateAxiosInstance(opts);
    }

    async post<T = any, D = ApiRequestBase & { [key: string]: any }>(url: string, data?: D, config?: AxiosRequestConfig & { [key: string]: any }) {
        let res = await this.axiosInstance.post<T, AxiosResponse<ApiResponse<T>>>(url, data, config);
        return res.data;
    }

    async postAndCheckRes<T = any, D = ApiRequestBase & { [key: string]: any }>(url: string, data?: D, config?: AxiosRequestConfig & { [key: string]: any }) {
        let res = await this.post<T, D>(url, data, config);
        if (!ApiResponse.isSuccess(res)) return Promise.reject(res);

        return res;
    }

    /**
     * 2.0.1获取平台配置信息
     */
    async getConfigInfo() {
        return (await this.postAndCheckRes<GetConfigInfoResponse>('config/platform/GetConfigInfo', null, { apiDesc: '2.0.1获取平台配置信息' })).data;
    }

    /**
     * SQL通道-list
     */
    async list(sql: string, parkId: number | string) {
        return (await this.postAndCheckRes<any[]>('dbproxy/list', { parkId, sql },
            { headers: { 'kt-lotcodes': parkId, version: '1.0' }, apiDesc: 'sql通道-list' })).data;
    }

    /**
     * SQL通道-selectOne
     */
    async selectOne(sql: string, parkId: number | string) {
        return (await this.postAndCheckRes<any>('dbproxy/selectOne', { parkId, sql },
            { headers: { 'kt-lotcodes': parkId, version: '1.0' }, apiDesc: 'sql通道-selectOne' })).data;
    }

    /**
     * SQL通道-execute
     */
    async execute(sql: string | string[], parkId: number | string, openTx?: 1 | 0) {
        return await this.post('dbproxy/execute',
            {
                sqlList: typeof sql === 'string' ? [sql] : sql,
                parkId,
                openTx
            },
            { headers: { 'kt-lotcodes': parkId, version: '1.0' }, apiDesc: 'sql通道-execute' });
    }
}