import Axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios';
import type { PureHttpError, PureHttpRequestConfig, PureHttpResponse, RequestMethods } from './types';
import NProgress from '../../utils/progress';
import { formatToken, getToken } from '@/utils/auth';
import { useUserStoreHook } from '@/store/system/user';
import { defaultMockConfig } from '@/api/service/config';

class PureHttp {
	/** `token`过期后，暂存待执行的请求 */
	private static requests = [];
	/** 防止重复刷新`token` */
	private static isRefreshing = false;
	/** 初始化配置对象 */
	private static initConfig: PureHttpRequestConfig = {};
	/** 保存当前`Axios`实例对象 */
	private static axiosInstance: AxiosInstance = Axios.create(defaultMockConfig);

	constructor() {
		this.httpInterceptorsRequest();
		this.httpInterceptorsResponse();
	}

	/** 重连原始请求 */
	private static retryOriginalRequest(config: PureHttpRequestConfig) {
		return new Promise(resolve => {
			PureHttp.requests.push((token: string) => {
				config.headers['token'] = formatToken(token);
				resolve(config);
			});
		});
	}

	/** 通用请求工具函数 */
	public request<T>(method: RequestMethods, url: string, param?: AxiosRequestConfig, axiosConfig?: PureHttpRequestConfig): Promise<T> {
		const config = {
			method,
			url,
			...param,
			...axiosConfig,
		} as PureHttpRequestConfig;

		// 单独处理自定义请求/响应回调
		return new Promise((resolve, reject) => {
			PureHttp.axiosInstance
				.request(config)
				.then((response: undefined) => {
					resolve(response);
				})
				.catch(error => {
					reject(error);
				});
		});
	}

	/** 单独抽离的`post`工具函数 */
	public post<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
		return this.request<T>('post', url, params, config);
	}

	/** 单独抽离的`get`工具函数 */
	public get<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
		return this.request<T>('get', url, params, config);
	}

	/** 请求拦截 */
	private httpInterceptorsRequest(): void {
		PureHttp.axiosInstance.interceptors.request.use(
			async (config: PureHttpRequestConfig): Promise<any> => {
				// 开启进度条动画
				NProgress.start();
				// 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
				if (typeof config.beforeRequestCallback === 'function') {
					config.beforeRequestCallback(config);
					return config;
				}
				if (PureHttp.initConfig.beforeRequestCallback) {
					PureHttp.initConfig.beforeRequestCallback(config);
					return config;
				}
				/** 请求白名单，放置一些不需要`token`的接口（通过设置请求白名单，防止`token`过期后再请求造成的死循环问题） */
				const whiteList = ['/refresh-token', '/login'];
				return whiteList.some(url => config.url.endsWith(url))
					? config
					: new Promise(resolve => {
							const data: any = getToken();
							if (data) {
								const now = new Date().getTime();
								const expired = parseInt(data.expires) - now <= 0;
								if (expired) {
									if (!PureHttp.isRefreshing) {
										PureHttp.isRefreshing = true;
										// token过期刷新
										useUserStoreHook()
											.handRefreshToken({ refreshToken: data.refreshToken })
											.then((res: any) => {
												const token = res.data.accessToken;
												config.headers['Authorization'] = formatToken(token);
												PureHttp.requests.forEach(cb => cb(token));
												PureHttp.requests = [];
											})
											.finally(() => {
												PureHttp.isRefreshing = false;
											});
									}
									resolve(PureHttp.retryOriginalRequest(config));
								} else {
									config.headers['Authorization'] = formatToken(data.accessToken);
									resolve(config);
								}
							} else {
								resolve(config);
							}
						});
			},
			error => {
				return Promise.reject(error);
			},
		);
	}

	/** 响应拦截 */
	private httpInterceptorsResponse(): void {
		const instance = PureHttp.axiosInstance;
		instance.interceptors.response.use(
			(response: PureHttpResponse) => {
				const $config = response.config;
				// 关闭进度条动画
				NProgress.done();
				// 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
				if (typeof $config.beforeResponseCallback === 'function') {
					$config.beforeResponseCallback(response);
					return response.data;
				}
				if (PureHttp.initConfig.beforeResponseCallback) {
					PureHttp.initConfig.beforeResponseCallback(response);
					return response.data;
				}
				return response.data;
			},
			(error: PureHttpError) => {
				const $error = error;
				$error.isCancelRequest = Axios.isCancel($error);
				// 关闭进度条动画
				NProgress.done();
				// 所有的响应异常 区分来源为取消请求/非取消请求
				return Promise.reject($error);
			},
		);
	}
}

export const http = new PureHttp();
