/**
 * @file request.ts
 * @description Axios 封装：支持自动取消重复请求、自定义业务成功码白名单、统一错误处理
 */

import axios, {
  type AxiosInstance,
  type AxiosRequestConfig,
  type AxiosResponse,
  type InternalAxiosRequestConfig,
} from 'axios'
import { nanoid } from 'nanoid'
import qs from 'qs'
import { showToast } from 'vant'
import { useUserStoreWithOut } from '@/stores'
import { BASE_URL, TIME_OUT } from './config'
import { serialize } from '@/utils/util'
import router from '@/router'

/* -------------------------------------------------------------------------- */
/*                                  类型声明                                  */
/* -------------------------------------------------------------------------- */

export interface CustomAxiosRequestConfig extends AxiosRequestConfig {
  /** 是否静默错误提示 */
  silent?: boolean
  /** 唯一请求 ID */
  nanoid?: string
}

export interface ServiceResponse<T = any> {
  code: string | number
  msg: string
  data: T
  timestamp?: string
}

/** 自定义错误类 */
export interface HttpErrorOptions {
  code?: string | number
  status?: number
  data?: any
  response?: any
}

export class HttpError extends Error {
  code?: string | number
  status?: number
  data?: any
  response?: any

  constructor(message: string, options: HttpErrorOptions = {}) {
    super(message)
    Object.assign(this, options)
  }
}

/* -------------------------------------------------------------------------- */
/*                              成功码白名单配置                              */
/* -------------------------------------------------------------------------- */

const SUCCESS_CODE_WHITE_LIST: (string | number)[] = [0, '0', 200, '200', '0000']

/* -------------------------------------------------------------------------- */
/*                             请求缓存与中止控制                              */
/* -------------------------------------------------------------------------- */

const requestMap = new Map<string, AbortController>()

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

const clearRequest = (key?: string) => {
  if (key) requestMap.delete(key)
}

const abortAllRequests = () => {
  for (const [key, controller] of requestMap) {
    controller.abort()
    requestMap.delete(key)
  }
}

/* -------------------------------------------------------------------------- */
/*                              网络错误统一处理                                */
/* -------------------------------------------------------------------------- */

const handleNetworkError = (response: AxiosResponse, silent?: boolean) => {
  const status = response?.status
  const backendCode = response?.data?.code
  const backendMsg = response?.data?.msg
  const backendData = response?.data?.data

  // 默认错误信息
  let message = '未知错误'

  const userStore = useUserStoreWithOut()

  // 先处理401登录失效问题
  if (status === 401) {
    showToast('您的登录状态已失效，请重新登录')
    abortAllRequests()

    const pushRecordId = userStore.pushRecordId
    // 🔁 跳转到登录页
    setTimeout(() => {
      router.replace({
        path: '/login',
        query: {
          pushRecordId: pushRecordId,
        },
      })
      useUserStoreWithOut().LogOut()
    }, 1500)
  }

  // ✅ 优先使用后端业务错误（即 code !== 0）
  if (backendCode && backendCode !== 0) {
    message = backendMsg || '业务处理失败'

    if (!silent) {
      // showToast(message)
    }

    // ✅ 抛出包含后端信息的 HttpError
    throw new HttpError(message, {
      code: backendCode,
      status,
      data: backendData,
      response: response.data,
    })
  }

  // 🚨 否则走 HTTP 状态码分支
  switch (status) {
    case 400:
      message = '错误的请求'
      break
    case 403:
      message = '拒绝访问'
      break
    case 404:
      message = '请求未找到'
      break
    case 408:
      message = '请求超时'
      break
    case 424:
      message = 'Token 无效'
      abortAllRequests()
      useUserStoreWithOut().LogOut()
      break
    case 500:
      message = '服务器错误'
      break
    case 502:
      message = '网关错误'
      break
    case 503:
      message = '服务不可用'
      break
    default:
      message = `其他错误：${status}`
  }

  const msg = backendMsg || backendData || message
  if (!silent) {
    showToast(msg)
  }

  throw new HttpError(msg, {
    code: backendCode || 'HTTP_ERROR',
    status,
    data: backendData,
    response: response.data,
  })
}

/* -------------------------------------------------------------------------- */
/*                                 请求拦截器                                  */
/* -------------------------------------------------------------------------- */

const onRequest = (config: CustomAxiosRequestConfig): CustomAxiosRequestConfig => {
  config.headers = config.headers || {}

  const userStore = useUserStoreWithOut()
  const { tenant_id, access_token } = userStore

  const requestKey = generateRequestKey(config)
  const prevController = requestMap.get(requestKey)

  // 🚫 若同一请求正在执行，取消上一个，只保留最后一次
  if (prevController) {
    prevController.abort(`重复请求已取消：${config.url}`)
    requestMap.delete(requestKey)
  }

  const controller = new AbortController()
  config.signal = controller.signal
  config.nanoid = nanoid()
  requestMap.set(requestKey, controller)

  const headers = config.headers as Record<string, any>
  if (tenant_id) {
    headers['TENANT-ID'] = tenant_id
  }

  if (access_token && headers.isToken !== false) {
    headers.Authorization = `Bearer ${access_token}`
  } else if (config.url?.includes('/auth/login/etssms')) {
    headers.Authorization = `Basic ZXRzOmV0cw==`
  }

  if (config.method === 'post' && headers.serialize) {
    config.data = serialize(config.data)
    delete headers.serialize
  }

  if (config.method === 'get') {
    config.paramsSerializer = (params) => qs.stringify(params, { arrayFormat: 'repeat' })
  }

  return config
}

/* -------------------------------------------------------------------------- */
/*                                 响应拦截器                                  */
/* -------------------------------------------------------------------------- */

/**
 * 为了解决 response.config 类型不兼容的问题，我们在这里通过类型断言
 * 将其“还原”为自定义的 CustomAxiosRequestConfig。
 */
const onResponse = (response: AxiosResponse<ServiceResponse>) => {
  const key = generateRequestKey(response.config)
  clearRequest(key)

  // ✅ 强制类型断言（解决 silent 类型丢失问题）
  // const customConfig = response.config as CustomAxiosRequestConfig

  const res = response.data
  // const { code, msg } = res

  // if (SUCCESS_CODE_WHITE_LIST.includes(code)) {
  //   return res
  // }

  // const url = response.config.url || ''
  // // 针对登录接口单独处理错误提示
  // if (url.includes('/auth/login/etssms')) {
  //   return res
  // }

  // if (code === 1 || !SUCCESS_CODE_WHITE_LIST.includes(code)) {
  //   const message = msg || '业务逻辑错误'
  //   if (customConfig.silent !== true) {
  //     showToast(msg)
  //   }
  //   throw new HttpError(message, { code, data: res })
  // }

  return res
}

const onResponseError = (error: any) => {
  const key = generateRequestKey(error.config || {})
  clearRequest(key)

  // ✅ 强制类型断言（解决 silent 类型丢失问题）
  const customConfig = error.config as CustomAxiosRequestConfig | undefined

  if (axios.isCancel(error)) {
    throw new HttpError('请求被取消', { code: 'CANCELLED' })
  }

  if (error.response) {
    handleNetworkError(error.response, customConfig?.silent)
  } else if (error.request) {
    throw new HttpError('请求超时或无响应', { code: 'TIMEOUT' })
  } else {
    throw new HttpError('请求配置异常', { code: 'REQUEST_ERROR' })
  }
}

/* -------------------------------------------------------------------------- */
/*                                 Request 类                                 */
/* -------------------------------------------------------------------------- */

export class Request {
  private instance: AxiosInstance

  constructor() {
    this.instance = axios.create({
      baseURL: BASE_URL,
      timeout: TIME_OUT,
    })

    this.instance.interceptors.request.use(
      onRequest as unknown as (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig,
    )
    this.instance.interceptors.response.use(
      onResponse as unknown as (res: AxiosResponse) => any,
      onResponseError,
    )
  }

  async request<T = any>(config: CustomAxiosRequestConfig): Promise<ServiceResponse<T>> {
    return await this.instance.request(config)
  }

  async get<T = any>(config: CustomAxiosRequestConfig): Promise<ServiceResponse<T>> {
    return await this.request<T>({ ...config, method: 'get' })
  }

  async post<T = any>(config: CustomAxiosRequestConfig): Promise<ServiceResponse<T>> {
    return await this.request<T>({ ...config, method: 'post' })
  }

  async put<T = any>(config: CustomAxiosRequestConfig): Promise<ServiceResponse<T>> {
    return await this.request<T>({ ...config, method: 'put' })
  }

  async delete<T = any>(config: CustomAxiosRequestConfig): Promise<ServiceResponse<T>> {
    return await this.request<T>({ ...config, method: 'delete' })
  }

  setSuccessCodes(codes: (string | number)[]) {
    SUCCESS_CODE_WHITE_LIST.splice(0, SUCCESS_CODE_WHITE_LIST.length, ...codes)
  }

  abortAll() {
    abortAllRequests()
  }
}
