import axios, {
  AxiosInstance,
  AxiosResponse,
  AxiosRequestConfig
} from "axios";
import { Des3Util } from "./Des3Util";
import { ApiResponse } from "./Dto";

export type Option = {
  url: string;

  /**
   * 是否加密
   */
  secure?: boolean;
  key?: string;
  iv?: string;

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

  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",
    },
  });

  let encrypt = (config: AxiosRequestConfig) => {
    //全局加密，当前接口不加密则不加密
    if (opt.secure === true && config["secure"] === false) return config;
    //全局不加密，当前接口也不加密则不加密，否则加密
    if (!opt.secure && !config["secure"]) return config;

    let data = config.data || {};

    let new_data = {
      data: Des3Util.encrypt(data, opt.key, opt.iv),
      imageInfo: data.imageInfo,
    };

    config.data = new_data;

    return config;
  };

  //请求拦截器是按添加顺序倒叙执行
  axiosInstance.interceptors.request.use(encrypt);
  axiosInstance.interceptors.request.use(
    opt.requestInterceptor?.onFulfilled,
    opt.requestInterceptor?.onRejected
  );
  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 = any>(
    url: string,
    data?: D,
    config?: AxiosRequestConfig & { apiDesc: string; secure?: boolean }
  ) {
    let res = await this.axiosInstance.post<
      T,
      AxiosResponse<ApiResponse<T>>
    >(url, data, config);
    return res.data;
  }

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

    return res;
  }
}
