import type { UseFetchOptions } from '#app'
import type { BasicResponse } from '~/models/common'
import type { RequestQueueItem, IInterceptor, IConfig, IOption } from './interface'
import { useLoginStore } from '~/stores/user/login'
import { getToken, getRefreshToken } from '../auth'
class Request {
  public baseURL: string
  public interceptor: IInterceptor
  private isRefreshing = false // 是否正在刷新token，开启请求队列
  private requestQueue: RequestQueueItem[] // 待请求队列

  constructor({ baseURL, interceptor }: IConfig) {
    this.baseURL = baseURL
    this.interceptor = interceptor as IInterceptor
    this.isRefreshing = false
    this.requestQueue = []
  }

  request<T = BasicResponse>({ url, method, params, data, options }: IOption<T>): Promise<T> {
    const newOptions: UseFetchOptions<T> = {
      baseURL: this.baseURL,
      method,
      query: params,
      body: data,
      ...options,
      onRequest: this.interceptor?.onRequest,
      onRequestError: this.interceptor?.onRequestError,
      onResponse: this.interceptor?.onResponse,
      onResponseError: this.interceptor?.onResponseError,
    }
    return new Promise((resolve, reject) => {
      this.requestPipeline(url, newOptions, resolve, reject)
    })
  }

  // 请求管道处理具体细节
  requestPipeline<T = BasicResponse>(
    url: string,
    options: UseFetchOptions<T>,
    resolve: (data: T) => void,
    reject: (data: unknown) => void
  ): void {
    const token = getToken()
    const newOptions = {
      ...options,
      headers: {
        Authorization: token ? `Bearer ${token}` : '',
        ...options?.headers,
      },
    }
    $fetch<T>(url, newOptions as any)
      .then((res) => {
        resolve(res as T)
      })
      .catch((error) => {
        if (error.status === 401) {
          if (!this.isRefreshing) {
            this.isRefreshing = true
            this.refreshToken()
          }
          this.addRequestQueueForRefreshToken<T>(url, options, resolve, reject)
        } else {
          reject(error)
        }
      })
  }

  // 刷新token
  refreshToken() {
    const loginStore = useLoginStore()
    this.postRefreshTokenFunc()
      .then((res) => {
        if (res.data) {
          const data = res.data
          loginStore.updateToken(data)
          this.requestQueueStartAfterRefreshToken()
        }
      })
      .catch(() => {
        loginStore.logout()
        navigateTo({ path: '/' })

        // ElMessage.error('登录已失效，需要重新登录')
        // navigateTo({ path: '/login' })
      })
      .finally(() => {
        this.isRefreshing = false
      })
  }
  postRefreshTokenFunc(): Promise<BasicResponse> {
    const data = {
      clientId: getCanvasFingerprint(),
    }
    const token = getRefreshToken()
    return $fetch(this.baseURL + '/auth/refresh', {
      method: 'post',
      body: data,
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
    })
  }

  // 添加请求到等待队列
  addRequestQueueForRefreshToken<T = BasicResponse>(
    url: string,
    options: UseFetchOptions<T>,
    resolve: (data: T) => void,
    reject: (data: unknown) => void
  ): void {
    this.requestQueue.push({
      url,
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      options,
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      resolve,
      reject,
    })
  }

  // 刷新token成功等待队列开始请求
  requestQueueStartAfterRefreshToken(): void {
    let requestQueueItem = this.requestQueue.pop()

    while (requestQueueItem) {
      const { options, url, resolve, reject } = requestQueueItem

      this.requestPipeline(url, options, resolve, reject)

      requestQueueItem = this.requestQueue.pop()
    }
  }
}

export default Request
