import { useUserSessionStore } from '@/store/userStore'
import { ResultEnum } from '#/enum'
import { t } from '@/locales/i18n'
import axios, { type AxiosError, type AxiosRequestConfig, type AxiosResponse } from 'axios'
import { nanoid } from 'nanoid'
import qs from 'qs'
import { toast } from 'sonner'
import { serialize } from '@/utils/util'
import { BASE_URL, TIME_OUT } from './config'

declare module 'axios' {
  interface InternalAxiosRequestConfig {
    nanoid?: string // 唯一id，用于中止请求
  }
}

const handleNetworkError = (response: AxiosResponse) => {
  const { actions } = useUserSessionStore.getState()
  if (!response) return
  let errMessage = '未知错误'
  const status = response?.status
  if (status) {
    switch (status) {
      case 400:
        errMessage = '错误的请求'
        break
      case 401:
        errMessage = '未授权，请重新登录'
        abortRequestFn()
        actions.logout()
        break
      case 403:
        errMessage = '拒绝访问'
        break
      case 404:
        errMessage = '请求错误,未找到该资源'
        break
      case 405:
        errMessage = '请求方法未允许'
        break
      case 408:
        errMessage = '请求超时'
        break
      case 424:
        errMessage = 'token无效'
        abortRequestFn()
        actions.logout()
        break
      case 500:
        errMessage = '服务器端出错'
        break
      case 501:
        errMessage = '网络未实现'
        break
      case 502:
        errMessage = '网络错误'
        break
      case 503:
        errMessage = '服务不可用'
        break
      case 504:
        errMessage = '网络超时'
        break
      case 505:
        errMessage = 'http版本不支持该请求'
        break
      default:
        errMessage = `其他连接错误 -- ${status}`
    }
  } else {
    errMessage = '无法连接到服务器！'
  }
  const message =
    status === 424
      ? response?.data?.msg || 'token已过期，请重新登陆'
      : response?.data?.data || response?.data?.msg
        ? response?.data?.data || response?.data?.msg
        : `${errMessage}`
  toast.error(message, {
    position: 'top-center'
  })
}

// 请求集合
const requestMap = new Map<string, AbortController>()

// 中止请求
const abortRequestFn = () => {
  for (const [mapKey, controller] of requestMap) {
    controller.abort() // 中止
    requestMap.delete(mapKey) // 清除
  }
}

// 创建 axios 实例
const axiosInstance = axios.create({
  baseURL: BASE_URL,
  timeout: TIME_OUT
})

// 请求拦截
axiosInstance.interceptors.request.use(
  (config) => {
    // 收集所有请求
    const mapKey = nanoid()
    const controller = new AbortController()
    config.signal = controller.signal
    config.nanoid = mapKey
    requestMap.set(mapKey, controller)

    const { tenant_id, access_token } = useUserSessionStore.getState()
    const isToken = (config.headers || {}).isToken === false
    if (access_token && !isToken) {
      config.headers.Authorization = 'Bearer ' + access_token // token
    }
    if (tenant_id) {
      config.headers['X-Tenant-Id'] = tenant_id // 租户ID
    }

    config.headers['X-Client-Type'] = 'TO_B'
    config.headers['X-Terminal-Type'] = 'SYS_WEB_PLT'

    // headers中配置serialize为true开启序列化
    if (config.method === 'post' && config.headers.serialize) {
      config.data = serialize(config.data)
      delete config.data.serialize
    }
    if (config.method === 'get') {
      config.paramsSerializer = function (params) {
        return qs.stringify(params, { arrayFormat: 'repeat' })
      }
    }
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

// 响应拦截
axiosInstance.interceptors.response.use(
  (res) => {
    const mapKey = res.config.nanoid
    requestMap.delete(mapKey as string)

    if (!res.data) throw new Error(t('sys.api.apiRequestFailed'))
    const { data, status, statusText } = res

    // 业务请求成功
    const hasSuccess = data && status === ResultEnum.SUCCESS
    if (hasSuccess) {
      return data
    }

    // 业务请求错误
    throw new Error(statusText || t('sys.api.apiRequestFailed'))
  },
  (err) => {
    // 清除当前请求
    const mapKey = err.config.nanoid
    requestMap.delete(mapKey as string)
    handleNetworkError(err.response)
    if (err.response) {
      // 请求已发出, 服务器用状态码响应
      return Promise.reject(err.response)
    } else if (err.request) {
      // 请求超时
      if (err.code === 'ECONNABORTED') {
        return Promise.reject({ data: { msg: '请求超时' } })
      } else {
        // 请求已经发出了, 但没有收到响应
        return Promise.reject({ data: { msg: '网络异常' } })
      }
    } else {
      // 在设置请求时触发了某些问题
      return Promise.reject({ data: { msg: '请求异常' } })
    }
  }
)

class Request {
  get<T = any, R = true>(
    config: AxiosRequestConfig
  ): Promise<R extends SERVICE.WrapDateType ? SERVICE.DataType<T> : T> {
    return this.request({ ...config, method: 'GET' })
  }

  post<T = any, R = true>(
    config: AxiosRequestConfig
  ): Promise<R extends SERVICE.WrapDateType ? SERVICE.DataType<T> : T> {
    return this.request({ ...config, method: 'POST' })
  }

  put<T = any, R = true>(
    config: AxiosRequestConfig
  ): Promise<R extends SERVICE.WrapDateType ? SERVICE.DataType<T> : T> {
    return this.request({ ...config, method: 'PUT' })
  }

  delete<T = any, R = true>(
    config: AxiosRequestConfig
  ): Promise<R extends SERVICE.WrapDateType ? SERVICE.DataType<T> : T> {
    return this.request({ ...config, method: 'DELETE' })
  }

  request<T = any, R = true>(
    config: AxiosRequestConfig
  ): Promise<R extends SERVICE.WrapDateType ? SERVICE.DataType<T> : T> {
    return new Promise((resolve, reject) => {
      axiosInstance
        .request(config)
        .then((res: AxiosResponse<any, any>['data']) => {
          resolve(res as unknown as R extends SERVICE.WrapDateType ? SERVICE.DataType<T> : T)
        })
        .catch((e: Error | AxiosError) => {
          reject(e)
        })
    })
  }
}
export default new Request()
