import { notification } from 'antd';
import Axios, {
  AxiosError,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios';
import { sessionStore } from '../store';

// 默认返回体
type Result = Partial<{
  code: number;
  message: string;
  data: any;
}>;

// 增强配置-用于自定义操作
interface AxiosRequestConfigPlus extends AxiosRequestConfig {
  skipErrorHandler?: boolean; // 是否跳过自定义报错操作
  errorMessage?: string; // 错误提示信息
}

// 增强配置-实例化axios
export type RequestInterceptor = (
  configs: InternalAxiosRequestConfig,
) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
export type ResponseInterceptor<T = any> = (
  response: AxiosResponse<T>,
) => AxiosResponse<T> | Promise<AxiosResponse<T>>;

interface AxiosCreateConfigPlus extends AxiosRequestConfig {
  successHandler?: (response: AxiosResponse) => boolean; // 成功结果的处理函数
  errorMessageHandler?: (response: AxiosResponse) => string; // 错误提示信息的处理函数
  requestInterceptorHandler?: (
    defaultRequestInterceptor: RequestInterceptor,
  ) => RequestInterceptor[]; // 请求前拦截的函数处理
  responseInterceptorHandler?: (
    defaultResponseInterceptor: ResponseInterceptor,
  ) => ResponseInterceptor[]; // 返回结果后拦截的函数处理
}

const codeMessage: Record<number, string> = {
  200: '请求成功',
  201: '对象创建成功',
  202: '请求已经被接受',
  204: '操作已经执行成功，但是没有返回数据',
  301: '资源已被移除',
  303: '重定向',
  304: '资源没有被修改',
  400: '参数列表错误（缺少，格式不匹配）',
  401: '用户没有权限（令牌、用户名、密码错误）。',
  403: '用户得到授权，但是访问是被禁止的(授权过期)。',
  404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',
  405: '不允许的http方法',
  409: '资源冲突，或者资源被锁',
  415: '不支持的数据，媒体类型',
  500: '服务器发生错误，请检查服务器。',
  501: '接口未实现',
  502: '网关错误。',
  503: '服务不可用，服务器暂时过载或维护。',
  504: '网关超时。',
};

export const create = (options?: AxiosCreateConfigPlus) => {
  const baseURL = '/';
  const timeout = 30000;

  const defaults = {
    baseURL,
    timeout,
  };

  const {
    successHandler: originalSuccessHandler,
    errorMessageHandler: originalErrorMessageHandler,
    requestInterceptorHandler,
    responseInterceptorHandler,
    ...restOptions
  } = options || {};

  const defaultAxios = Axios.create({ ...defaults, ...restOptions });

  const defaultSuccessHandler = (response: AxiosResponse<Result>) => {
    return response.data.code === 200;
  };
  const defaultErrorMessageHandler = (response: AxiosResponse<Result>) => {
    return response.data.message || '';
  };

  const defaultRequestInterceptor: RequestInterceptor = (configs) => {
    const loginInfo = sessionStore.get('login-info');
    configs.headers['Authorization'] =
      configs.headers['Authorization'] || loginInfo?.access_token || '';
    configs.headers['COW-TOKEN'] =
      configs.headers['COW-TOKEN'] || loginInfo?.secretCode || '';
    return configs;
  };

  if (requestInterceptorHandler) {
    const requestInterceptors = requestInterceptorHandler(
      defaultRequestInterceptor,
    );
    requestInterceptors.forEach((interceptor) => {
      defaultAxios.interceptors.request.use(interceptor);
    });
  } else {
    defaultAxios.interceptors.request.use(defaultRequestInterceptor);
  }

  const successHandler = originalSuccessHandler || defaultSuccessHandler;
  const errorMessageHandler =
    originalErrorMessageHandler || defaultErrorMessageHandler;

  const defaultResponseInterceptorHandler: ResponseInterceptor<Result> = (
    response,
  ) => {
    const { config, request } = response;
    if (successHandler(response)) {
      return Promise.resolve(response);
    }
    const message = errorMessageHandler(response);
    const code = 'ERR_SERVER';
    const error = new AxiosError(message, code, config, request, response);
    return Promise.reject(error);
  };

  if (responseInterceptorHandler) {
    const responseInterceptors = responseInterceptorHandler(
      defaultResponseInterceptorHandler,
    );
    responseInterceptors.forEach((interceptor) => {
      defaultAxios.interceptors.response.use(interceptor);
    });
  } else {
    defaultAxios.interceptors.response.use(defaultResponseInterceptorHandler);
  }

  async function requestHandler<T = any>(
    configs: AxiosRequestConfigPlus,
  ): Promise<T> {
    try {
      const { data } = await defaultAxios(configs);
      return Promise.resolve(data);
    } catch (catchErr) {
      const error = catchErr as AxiosError<Result>;
      const { skipErrorHandler, errorMessage } = configs;
      if (!skipErrorHandler) {
        const { code: codeString, message, request } = error;
        if (codeString === 'ERR_SERVER') {
          const errorText = errorMessage || message;
          notification.error({
            message: errorText,
          });
        } else {
          const code = request?.status as number;
          const errorText = errorMessage || codeMessage[code];
          notification.error({
            message: errorText,
          });
        }
      }
      return Promise.reject(error);
    }
  }
  return requestHandler;
};

// @ts-ignore
export const baseURL = window.requestBase || '/';

const defaultRequest = create({ baseURL });

export default defaultRequest;

export const downloadRequest = create({
  baseURL,
  method: 'get',
  responseType: 'blob',
  responseInterceptorHandler: () => {
    const defaultResponseInterceptorHandler: ResponseInterceptor = async (
      response,
    ) => {
      const { config, request, data } = response;
      let text = await data.text();
      try {
        text = JSON.parse(text);
      } catch (error) {}
      if (typeof text === 'string') {
        return Promise.resolve(response);
      }
      response.data = text;
      const message = text.message;
      const code = 'ERR_SERVER';
      const error = new AxiosError(message, code, config, request, response);
      return Promise.reject(error);
    };
    return [defaultResponseInterceptorHandler];
  },
});
export const downloadExcelRequest = async (
  options: AxiosRequestConfig,
  name: string,
) => {
  const blob = await downloadRequest(options);
  const type = 'application/vnd.ms-excel;charset=utf-8'; //excel文件
  const u = window.URL.createObjectURL(new Blob([blob], { type }));
  const a = document.createElement('a');
  a.download = name;
  a.href = u;
  a.style.display = 'none';
  document.body.appendChild(a);
  a.click();
  a.remove();
};
