import Axios, {
	type AxiosInstance,
	type AxiosRequestConfig,
	type CustomParamsSerializer,
	type AxiosResponse,
	type InternalAxiosRequestConfig,
	type AxiosError
} from 'axios'
import { stringify } from 'qs'

// 基础配置
const defaultConfig: AxiosRequestConfig = {
	timeout: 6000,
	headers: {
		'Content-Type': 'application/json;charset=utf-8'
	},
	paramsSerializer: {
		serialize: stringify as unknown as CustomParamsSerializer
	}
}

// 响应数据基础结构
export interface BaseResponse {
	code: number
	error?: string
	isAuthorized?: boolean
	isSuccess?: boolean
}

type OmitBaseResponse<T> = Omit<T, keyof BaseResponse>

export type ResponseData<T = any> = BaseResponse & OmitBaseResponse<T>

export type ResponseValidator<T = any> = (data: ResponseData<T>) => boolean

export type ResponseDataInterceptor<T = any> = (data: ResponseData<T>) => void

let responseDataInterceptor: ResponseDataInterceptor | undefined

export const setResponseDataInterceptor = (interceptor?: ResponseDataInterceptor) => {
	responseDataInterceptor = interceptor
}

// 重试配置
export interface RetryConfig {
	retries?: number
	retryDelay?: number
	retryCondition?: (error: AxiosError) => boolean
}

// 拦截器配置类型
interface InterceptorsConfig {
	requestInterceptor?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig
	requestErrorInterceptor?: (error: AxiosError) => Promise<any>
	responseInterceptor?: (response: AxiosResponse<ResponseData<any>>) => any
	responseErrorInterceptor?: (error: AxiosError) => Promise<any>
}

// 请求唯一键
type RequestKey = string | symbol

/**
 * 获取url参数
 * @param url
 * @returns
 */
export function getParams(url: string) {
	const paramsString = url.split('?')[1]
	const searchParams = new URLSearchParams(paramsString)
	return Object.fromEntries(searchParams.entries())
}

class HttpClient {
	private instance: AxiosInstance
	private requestInterceptorId?: number
	private responseInterceptorId?: number
	private abortControllers: Map<RequestKey, AbortController> = new Map()

	constructor(customConfig?: AxiosRequestConfig, interceptors?: InterceptorsConfig) {
		this.instance = Axios.create({ ...defaultConfig, ...customConfig })
		this.initInterceptors(interceptors)
	}

	private initInterceptors(interceptors?: InterceptorsConfig): void {
		this.initRequestInterceptor(
			interceptors?.requestInterceptor,
			interceptors?.requestErrorInterceptor
		)
		this.initResponseInterceptor(
			interceptors?.responseInterceptor,
			interceptors?.responseErrorInterceptor
		)
	}

	private initRequestInterceptor(
		customInterceptor?: InterceptorsConfig['requestInterceptor'],
		customErrorInterceptor?: InterceptorsConfig['requestErrorInterceptor']
	): void {
		const defaultInterceptor = (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
			// const search = getParams(window.location.href)
			// const enterpriseCode = search?.['enterpriseCode'] || 'a'
			const token =
				localStorage.getItem('oauth2token') ||
				document.cookie.match(new RegExp('(^| )' + 'x-sessionId_b' + '=([^;]*)(;|$)'))?.[2]

			// 添加token
			if (token) {
				if (!config.headers) {
					config.headers = {} as InternalAxiosRequestConfig['headers']
				}
				config.headers.Authorization = JSON.parse(token) || token
			}

			return config
		}

		// 默认请求错误拦截器
		const defaultErrorInterceptor = (error: AxiosError): Promise<any> => {
			// todo 错误处理

			return Promise.reject(error)
		}

		this.requestInterceptorId = this.instance.interceptors.request.use(
			customInterceptor || defaultInterceptor,
			customErrorInterceptor || defaultErrorInterceptor
		)
	}

	private initResponseInterceptor(
		customInterceptor?: InterceptorsConfig['responseInterceptor'],
		customErrorInterceptor?: InterceptorsConfig['responseErrorInterceptor']
	): void {
		const defaultInterceptor = (
			response: AxiosResponse<ResponseData<any>>
		): AxiosResponse<ResponseData<any>> => {
			// 会话丢失 重定向到/登录页
			if (response.data?.code === 9999) {
				if (import.meta.env.DEV) {
					window.location.href =
						'/Views/Account/Index.html?cb=' + encodeURIComponent(window.location.href)
				} else {
					window.location.href = '/'
				}
			}

			responseDataInterceptor?.(response.data)

			return response
		}

		this.responseInterceptorId = this.instance.interceptors.response.use(
			customInterceptor || defaultInterceptor,
			customErrorInterceptor
		)
	}

	private getRequestKey(config: AxiosRequestConfig): RequestKey | undefined {
		if (!config.url) return undefined
		const method = config.method?.toUpperCase() || 'GET'
		const paramsKey = this.serializeRequestPart(config.params)
		const dataKey = this.serializeRequestPart(config.data)
		return `${method}-${config.url}-${paramsKey}-${dataKey}`
	}

	private serializeRequestPart(value: unknown): string {
		if (value === undefined || value === null || value === '') return ''
		if (value instanceof URLSearchParams) return value.toString()
		if (typeof FormData !== 'undefined' && value instanceof FormData) {
			return Array.from(value.entries())
				.map(([key, item]) => `${key}:${this.serializeRequestPart(item)}`)
				.sort()
				.join('&')
		}
		if (typeof Blob !== 'undefined' && value instanceof Blob) {
			return `${value.type}:${value.size}`
		}
		if (typeof value !== 'object') return String(value)
		return JSON.stringify(this.sortRequestPart(value))
	}

	private sortRequestPart(value: unknown): unknown {
		if (Array.isArray(value)) {
			return value.map((item) => this.sortRequestPart(item))
		}
		if (!value || typeof value !== 'object') return value
		if (value instanceof Date) return value.toISOString()
		if (value instanceof URLSearchParams) return value.toString()
		if (typeof FormData !== 'undefined' && value instanceof FormData) {
			return Array.from(value.entries())
				.map(([key, item]) => [key, this.sortRequestPart(item)])
				.sort(([left], [right]) => String(left).localeCompare(String(right)))
		}
		if (typeof Blob !== 'undefined' && value instanceof Blob) {
			return `${value.type}:${value.size}`
		}

		return Object.keys(value as Record<string, unknown>)
			.sort()
			.reduce(
				(acc, key) => {
					acc[key] = this.sortRequestPart((value as Record<string, unknown>)[key])
					return acc
				},
				{} as Record<string, unknown>
			)
	}

	private setupCancelController(
		config: AxiosRequestConfig,
		requestKey?: RequestKey
	): AxiosRequestConfig {
		const key = requestKey || this.getRequestKey(config)
		if (!key) return config

		// 如果已有相同key的请求，先取消它
		this.cancelRequest(key)

		const controller = new AbortController()
		this.abortControllers.set(key, controller)

		return {
			...config,
			signal: controller.signal
		}
	}

	public removeRequestInterceptor(): void {
		if (this.requestInterceptorId !== undefined) {
			this.instance.interceptors.request.eject(this.requestInterceptorId)
			this.requestInterceptorId = undefined // 重置ID，避免重复移除
		}
	}

	public removeResponseInterceptor(): void {
		if (this.responseInterceptorId !== undefined) {
			this.instance.interceptors.response.eject(this.responseInterceptorId)
			this.responseInterceptorId = undefined // 重置ID，避免重复移除
		}
	}

	public setRequestInterceptor(
		customInterceptor?: InterceptorsConfig['requestInterceptor'],
		customErrorInterceptor?: InterceptorsConfig['requestErrorInterceptor']
	): void {
		this.removeRequestInterceptor()
		this.initRequestInterceptor(customInterceptor, customErrorInterceptor)
	}

	public setResponseInterceptor(
		customInterceptor?: InterceptorsConfig['responseInterceptor'],
		customErrorInterceptor?: InterceptorsConfig['responseErrorInterceptor']
	): void {
		this.removeResponseInterceptor()
		this.initResponseInterceptor(customInterceptor, customErrorInterceptor)
	}

	public getInstance(): AxiosInstance {
		return this.instance
	}

	public cancelRequest(key: RequestKey, message?: string): boolean {
		const controller = this.abortControllers.get(key)
		if (controller) {
			controller.abort(message || `取消请求: ${String(key)}`)
			this.abortControllers.delete(key)
			return true
		}
		return false
	}

	public cancelAllRequests(message?: string): void {
		this.abortControllers.forEach((controller, key) => {
			controller.abort(message || `取消所有请求: ${String(key)}`)
		})
		this.abortControllers.clear()
	}

	public static isCancel(error: unknown): boolean {
		return Axios.isCancel(error)
	}

	private sleep(ms: number): Promise<void> {
		return new Promise((resolve) => setTimeout(resolve, ms))
	}

	/**
	 * 通用请求方法
	 * @param url 请求地址
	 * @param config 请求配置
	 * @returns 响应数据
	 */
	public async request<T = any>(
		url: string,
		config?: AxiosRequestConfig & { requestKey?: RequestKey; retry?: RetryConfig }
	): Promise<ResponseData<T>> {
		const { requestKey, retry, method, ...restConfig } = config || {}

		const defaultRetryCondition = (error: AxiosError) => {
			// 默认只重试网络错误或5xx服务器错误
			return !error.response || (error.response.status >= 500 && error.response.status < 600)
		}

		const retryConfig = {
			retries: 0,
			retryDelay: 1000,
			retryCondition: defaultRetryCondition,
			...retry
		}

		let lastError: any
		const key = requestKey || this.getRequestKey({ ...restConfig, method, url })

		for (let attempt = 0; attempt <= retryConfig.retries; attempt++) {
			try {
				if (attempt > 0 && key) {
					this.abortControllers.delete(key)
				}

				const requestConfig = this.setupCancelController({ ...restConfig, method, url }, requestKey)

				const response = await this.instance.request<ResponseData<T>>(requestConfig)

				return response.data
			} catch (error) {
				lastError = error
				if (
					attempt === retryConfig.retries ||
					!retryConfig.retryCondition(error as AxiosError) ||
					HttpClient.isCancel(error)
				) {
					break
				}

				// 延迟后重试
				if (retryConfig.retryDelay > 0) {
					await this.sleep(retryConfig.retryDelay)
				}
			}
		}

		return Promise.reject(lastError)
	}

	/**
	 * GET 请求
	 * @param url 请求地址
	 * @param config 请求配置
	 * @returns 响应数据
	 */
	public get<T = any>(
		url: string,
		config?: AxiosRequestConfig & { requestKey?: RequestKey; retry?: RetryConfig }
	): Promise<ResponseData<T>> {
		return this.request<T>(url, { ...config, method: 'GET' })
	}

	/**
	 * POST 请求
	 * @param url 请求地址
	 * @param data 请求数据
	 * @param config 请求配置
	 * @returns 响应数据
	 */
	public post<T = any>(
		url: string,
		data?: any,
		config?: AxiosRequestConfig & { requestKey?: RequestKey; retry?: RetryConfig }
	): Promise<ResponseData<T>> {
		return this.request<T>(url, { ...config, data, method: 'POST' })
	}

	/**
	 * PUT 请求
	 * @param url 请求地址
	 * @param data 请求数据
	 * @param config 请求配置
	 * @returns 响应数据
	 */
	public put<T = any>(
		url: string,
		data?: any,
		config?: AxiosRequestConfig & { requestKey?: RequestKey; retry?: RetryConfig }
	): Promise<ResponseData<T>> {
		return this.request<T>(url, { ...config, data, method: 'PUT' })
	}

	/**
	 * DELETE 请求
	 * @param url 请求地址
	 * @param config 请求配置
	 * @returns 响应数据
	 */
	public delete<T = any>(
		url: string,
		config?: AxiosRequestConfig & { requestKey?: RequestKey; retry?: RetryConfig }
	): Promise<ResponseData<T>> {
		return this.request<T>(url, { ...config, method: 'DELETE' })
	}

	/**
	 * PATCH 请求
	 * @param url 请求地址
	 * @param data 请求数据
	 * @param config 请求配置
	 * @returns 响应数据
	 */
	public patch<T = any>(
		url: string,
		data?: any,
		config?: AxiosRequestConfig & { requestKey?: RequestKey; retry?: RetryConfig }
	): Promise<ResponseData<T>> {
		return this.request<T>(url, { ...config, data, method: 'PATCH' })
	}
}

const http = new HttpClient()
// 导出request供api-service使用
export const request = http.request.bind(http)

export default http
