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

import { v4 as uuidv4 } from 'uuid';

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

  appId: string;
  appSecret: string;

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

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

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

  /**
   * 请求拦截器
   *
   * 在签名拦截器后执行
   */
  requestInterceptor?: {
    onFulfilled?: (
      value: AxiosRequestConfig
    ) => AxiosRequestConfig | Promise<AxiosRequestConfig>;
    onRejected?: (error: any) => any;
  };

  /**
   * 响应拦截器
   */
  responseInterceptor?: {
    onFulfilled?: (
      value: AxiosResponse<any>
    ) => AxiosResponse<any> | Promise<AxiosResponse<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 sign_interceptor = (config: AxiosRequestConfig) => {
    let data = (config.data || {}) as ApiRequestBase;

    //填充serviceCode
    if (!data.serviceCode && config.url) {
      let serviceCode = config.url.split("/").pop();
      data.serviceCode =
        serviceCode.charAt(0).toLowerCase() + serviceCode.slice(1);
    }

    //签名
    if (!data.key) {
      data.appId = opt.appId;
      data.reqId = uuidv4().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(sign_interceptor);
  axiosInstance.interceptors.response.use(
    opt.responseInterceptor?.onFulfilled,
    opt.responseInterceptor?.onRejected
  );

  return axiosInstance;
}

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

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

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

  async postAndCheckRes<T = any, D = ApiRequest>(
    url: string,
    data?: D,
    config?: AxiosRequestConfig & { apiDesc: string }
  ) {
    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",
      }
    );
  }
}
