import { AxiosError, type AxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus'
import cache from '@/utils/cache'
import router from '@/router'
import { useUserStore } from '@/stores/modules/user'
import { Axios, type AxiosHooks } from './axios'
import settings from '@/settings'

const requestRecords: any = {}

function generateRequestKey(config: AxiosRequestConfig): string {
  const { url, method, params, data } = config
  return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
}

const axiosHooks: AxiosHooks = {
  requestInterceptorsHook(config) {
    const { withToken, isParamsToData, repeatSubmit } = config.requestOptions
    const params = config.params || {}
    const headers = config.headers || {}
    if (withToken) {
      const token = cache.get('token')
      if (token) {
        headers.token = token
      }
    }
    if (isParamsToData && !Reflect.has(config, 'data') && config.method?.toUpperCase() === 'POST') {
      config.data = params
      config.params = {}
    }
    const isRepeatSubmit = headers.repeatSubmit !== false
    const isMutableMethod = ['post', 'put', 'patch', 'delete'].includes(config.method || '')
    const repeatSubmitConfig = {
      enabled: true, // 是否开启重复提交防止
      interval: 1000, // 重复提交间隔时间，单位毫秒
      message: '数据正在处理，请勿重复提交',
      ...repeatSubmit
    }
    if (isRepeatSubmit && repeatSubmitConfig.enabled && isMutableMethod) {
      const requestKey = generateRequestKey(config)
      const currentTime = new Date().getTime()
      const requestData = typeof config.data === 'object' ? JSON.stringify(config.data) : String(config.data)
      if (requestRecords[requestKey]) {
        const timeDiff = currentTime - requestRecords[requestKey].time
        if (timeDiff < repeatSubmitConfig.interval && requestRecords[requestKey].data === requestData) {
          console.warn(`[${config.url}]: ${repeatSubmitConfig.message}`)
          return Promise.reject(new Error(repeatSubmitConfig.message))
        }
      }
      requestRecords[requestKey] = {
        url: config.url || '',
        data: requestData,
        time: currentTime
      }
      setTimeout(() => {
        delete requestRecords[requestKey]
      }, repeatSubmitConfig.interval)
    }
    config.headers = headers
    return config
  },
  requestInterceptorsCatchHook(err) {
    return err
  },
  async responseInterceptorsHook(response) {
    const { isTransformResponse, isReturnDefaultResponse, showErrorMessage } = response.config.requestOptions
    if (isReturnDefaultResponse) {
      return response
    }
    if (!isTransformResponse) {
      return response.data
    }
    const { code, data, msg } = response.data
    switch (code) {
      case 1:
        return data
      case 0:
        if (showErrorMessage !== false) {
          ElMessage.error(msg || '请求失败')
        }
        return Promise.reject(data)
      default:
        return data
    }
  },
  responseInterceptorsCatchHook(error) {
    if (error.code !== AxiosError.ERR_CANCELED) {
      const status = error.response?.status
      if (status === 401) {
        const userStore = useUserStore()
        userStore.resetAuthInfo()
        router.replace({
          path: '/login',
          query: { redirect: router.currentRoute.value.fullPath }
        })
      } else {
        error.message && ElMessage.error(error.message)
      }
    }
    return Promise.reject(error)
  }
}

const defaultOptions: AxiosRequestConfig = {
  //接口超时时间
  timeout: 30000,
  // 基础接口地址
  baseURL: import.meta.env.VITE_API_BASE_URL,
  //请求头
  headers: { 'Content-Type': 'application/json;charset=UTF-8' },
  // 处理 axios的钩子函数
  axiosHooks: axiosHooks,
  // 每个接口可以单独配置
  requestOptions: {
    // 是否将params视为data参数，仅限post请求
    isParamsToData: true,
    //是否返回默认的响应
    isReturnDefaultResponse: false,
    // 需要对返回数据进行处理
    isTransformResponse: true,
    // 接口拼接地址
    urlPrefix: settings.urlPrefix,
    // 是否携带token
    withToken: true
  }
}

function createAxios(opt?: Partial<AxiosRequestConfig>) {
  const mergedOptions = {
    ...defaultOptions,
    ...opt,
    requestOptions: {
      ...defaultOptions.requestOptions,
      ...opt?.requestOptions
    },
    headers: {
      ...defaultOptions.headers,
      ...opt?.headers
    },
    axiosHooks: {
      ...defaultOptions.axiosHooks,
      ...opt?.axiosHooks
    }
  }
  return new Axios(mergedOptions)
}

const request = createAxios()
export default request
