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

import {
  UniteApiError
} from "./UniteApiError";

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

  appId: string;
  appSecret: string;

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

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

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

function randomUUID() {
  const buf = crypto.randomBytes(16);
  buf[6] = (buf[6] & 0x0f) | 0x40;
  buf[8] = (buf[8] & 0x3f) | 0x80;
  return buf.toString('hex');
}

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

  constructor(opts: Options) {
    let axiosInstance = axios.create({
      baseURL: opts.url,
      timeout: opts.timeout || 10000,
      headers: {
        "Content-Type": "application/json;charset=utf-8",
        version: opts.version || "1.0.0",
        "accept-language": opts.lang || "zh-CN",
      },
    });

    axiosInstance.interceptors.request.use(config => {
      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 = opts.appId;
        if (!data.reqId) data.reqId = randomUUID();
        data.ts = Date.now().toString();
        data.key = SignUtil.sign(data, opts.appSecret);
      }

      config.data = data;

      return config;
    });

    this.axiosInstance = axiosInstance;
  }

  async post<T = any, D = ApiRequest>(
    url: string,
    data?: D,
    config?: AxiosRequestConfig & { apiDesc?: string }
  ) {
    let res = await this.axiosInstance.post<
      ApiResponse<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(new UniteApiError(res.resMsg, res.resCode));

    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<T = any>(sql: string, parkId: number | string, timeout: number = 10000) {
    return (
      await this.postAndCheckRes<T[]>(
        "dbproxy/list",
        { parkId, sql },
        {
          headers: { "kt-lotcodes": parkId, version: "1.0" },
          apiDesc: "sql通道-list",
          timeout: timeout
        }
      )
    ).data;
  }

  /**
   * SQL通道-selectOne
   */
  async selectOne<T = any>(sql: string, parkId: number | string) {
    return (
      await this.postAndCheckRes<T>(
        "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",
      }
    );
  }
}
