import axios, { AxiosInstance, AxiosResponse, AxiosRequestConfig } from 'axios';
import React, { useContext } from 'react';
import _ from 'lodash';
import localStorageKey from '@/utils/localStorageKey';
import { BACKEND_API_URI_PREFIX } from './api_prefix';
import qs from 'qs';



export interface IAxiosRequestConfig extends AxiosRequestConfig {
    /** 取消全局错误提示 */
    noToastWarn?: boolean;
    /** 允许截流 */
    allowIntercept?: boolean;
}

export type HttpConfig = IAxiosRequestConfig;

export interface IRequest {
  method: <T>(config: AxiosRequestConfig) => Promise<T>;
  config: IAxiosRequestConfig;
}

export class Http {
  instance: AxiosInstance;

  /** 请求方法集合
   * key-唯一标识
   */
  requestList: {
    [key: string]: IRequest[];
  } = {};

  /** 请求锁 */
  isRequesting: { [key: string]: boolean } = {};

  constructor(defaultConfig: IAxiosRequestConfig = {}) {
    this.instance = axios.create(defaultConfig);
  }

  /** 更新回调函数数组 */
  setRequestList = (key: string, params: IRequest) => {
    this.requestList = {
      [key]: this.requestList[key] ? [...this.requestList[key], params] : [params],
    };
  };

    /** 处理成功请求
   * first-首次请求 last-最后一次请求 key-请求路径
   */
     handleSuccess = ({ key, resolve, res: firstRes }: { key: string; resolve: any; res: any }) => {
        this.isRequesting[key] = false;
        const requestLen = this.requestList[key]?.length || 0;
        if (requestLen >= 2) {
          const first = this.requestList[key][0];
          const last = this.requestList[key][requestLen - 1];
          if (!_.isEqual(first.config, last.config)) {
            // @ts-ignore
            last.method<T>(last.config).then((lastRes: AxiosResponse<any>) => {
              resolve(lastRes?.data);
            });
          } else {
            resolve(firstRes.data);
          }
        } else {
          resolve(firstRes.data);
        }
        this.requestList[key] = [];
      };


  /** 请求截流
   * 当多次发起请求并且第一次请求未结束时，若参数完全一致只允许首次请求，不一致在首次结束后，进行最后一次
   */
  requestInterception<T>(config: IAxiosRequestConfig): Promise<T> {
    const requestMethod = this.instance.request;
    const key: string = config.url || '';
    return new Promise((resolve, reject) => {
      if (this.isRequesting[key]) {
        this.setRequestList(key, { method: requestMethod, config });
      } else {
        this.isRequesting[key] = true;
        this.setRequestList(key, { method: requestMethod, config });

        requestMethod<T>(config)
          .then((res: AxiosResponse<any>) => {
              /** 可以接入日志监控 */
            // const { code, msg = '' } = res.data;
            // if (code !== 200 && code !== 0) {
            //   this.handleSentry({
            //     ...sentryParams,
            //     msg,
            //     code,
            //   });
            // }
            this.handleSuccess({
              res,
              key,
              resolve,
            });
          })
          .catch((e) => {
              /** 可以接入日志监控 */
            // this.handleSentry({
            //   ...sentryParams,
            //   msg: e.error || '',
            //   code: e.code || '',
            // });
            reject(e);
          });
      }
    });
  }
  request<T>(config: IAxiosRequestConfig) {
    const requestMethod = this.instance.request<T>(config);
    return requestMethod.then((res) => res.data);
  }

  getConfig(config?: IAxiosRequestConfig, data?: any ) {
    let Authorization: string =
    `Bearer ` +
      JSON.parse(localStorage.getItem(localStorageKey.TOKEN) || "{}")
        ?.access_token || "";

        const newConfig = {
          headers:{Authorization, ...config?.headers, },...config
        }
        if (
          newConfig?.headers['Content-Type'] === "application/x-www-form-urlencoded"
        ) {
          newConfig.data = qs.stringify(data);
        }
    return newConfig
  }


  get<T>(url: string, config?: IAxiosRequestConfig) {
    if (config?.allowIntercept) {
      return this.requestInterception<T>({ url: `${BACKEND_API_URI_PREFIX}${url}`, method: 'GET', ...this.getConfig(config) });
    }
    return this.request<T>({ url: `${BACKEND_API_URI_PREFIX}${url}`, method: 'GET', ...this.getConfig(config) });
  }

  patch<T>(url: string, data?: any, config?: IAxiosRequestConfig) {
    return this.request<T>({ method: 'PATCH', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config, data) });
  }

  put<T>(url: string, data?: any, config?: IAxiosRequestConfig) {
    return this.request<T>({ method: 'PUT', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config, data) });
  }

  post<T>(url: string, data?: any, config?: IAxiosRequestConfig) {
    if (config?.allowIntercept) {
      return this.requestInterception<T>({ method: 'POST', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config, data) });
    }
    return this.request<T>({ method: 'POST', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config, data) });
  }

  del<T>(url: string, config?: IAxiosRequestConfig) {
    return this.request<T>({ method: 'DELETE', url: `${BACKEND_API_URI_PREFIX}${url}`, ...this.getConfig(config) });
  }
}

const rawHttp = new Http();

const HttpContext = React.createContext<Http>(rawHttp);

export const HttpProvider = HttpContext.Provider;

export function useHttp(raw?: boolean) {
  const httpClient = useContext(HttpContext);
  return raw ? rawHttp : httpClient;
}

export default rawHttp;

export interface IResponse<T> {
  errorCode?: number;
  massage?: string;
  data: T;
}
