import axios from 'axios'
import type { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import { showToast } from 'vant'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { apiConfig } from '@/config/env'
import type { ApiResponse } from '@/types/api'
import router from '@/router'

// 关闭 NProgress 右侧旋转小圆圈，保持进度条简洁
NProgress.configure({ showSpinner: false })

// localStorage 中存储 token 的 key，与登录/登出逻辑保持一致
const TOKEN_KEY = 'token'

// 创建 axios 实例，baseURL 和 timeout 从环境变量配置中读取
const service: AxiosInstance = axios.create({
  baseURL: apiConfig.baseURL, // 接口基础地址，由 .env 文件中的 VITE_API_BASE_URL 决定
  timeout: apiConfig.timeout, // 请求超时时间（毫秒），默认 10000
})

/**
 * 请求拦截器
 * 在每个请求发出前执行：
 * 1. 启动顶部进度条
 * 2. 自动携带 JWT token 到请求头
 */
service.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 开始加载进度条
    NProgress.start()

    // 从本地存储读取 token，存在则添加到请求头
    const token = localStorage.getItem(TOKEN_KEY)
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }

    return config
  },
  // 请求配置出错时（如网络断开），结束进度条并抛出错误
  (error) => {
    NProgress.done()
    return Promise.reject(error)
  },
)

/**
 * 响应拦截器
 * 在收到响应后执行：
 * 1. 结束进度条
 * 2. 判断后端业务 code，非 0 视为业务失败
 * 3. HTTP 状态码异常时给出中文提示
 * 4. 401 统一清除 token 并跳转登录页
 */
service.interceptors.response.use(
  // ——— 成功响应（HTTP 2xx）———
  (response: AxiosResponse<ApiResponse>) => {
    NProgress.done()

    // 后端返回的数据结构：{ code, message, data }
    const res = response.data

    // 业务层错误：后端返回了 HTTP 200，但 code 不为 0，表示业务处理失败
    if (res.code !== 0) {
      showToast(res.message || '请求失败')

      // token 过期或无效，清除本地 token 并跳转登录页
      if (res.code === 401) {
        localStorage.removeItem(TOKEN_KEY)
        router.push('/login')
      }

      // 将业务错误抛出，调用方可通过 catch 捕获
      return Promise.reject(new Error(res.message || '请求失败'))
    }

    // 业务成功，直接返回 data 字段，调用方无需再解包 response.data.data
    return res as unknown as AxiosResponse
  },

  // ——— 失败响应（HTTP 非 2xx 或网络错误）———
  (error) => {
    NProgress.done()

    // HTTP 状态码 → 中文提示映射表
    const statusCodeMap: Record<number, string> = {
      400: '请求参数错误',
      401: '未授权，请重新登录',
      403: '拒绝访问',
      404: '请求资源不存在',
      500: '服务器内部错误',
    }

    // 优先取映射表中的提示，没有则显示状态码；无 response 说明是网络异常
    const status = error.response?.status as number
    const message = error.response
      ? statusCodeMap[status] || `连接错误 ${status}`
      : '网络连接异常'

    showToast(message)

    // 401 未授权：清除 token 并跳转登录页
    if (error.response?.status === 401) {
      localStorage.removeItem(TOKEN_KEY)
      router.push('/login')
    }

    return Promise.reject(error)
  },
)

/**
 * 请求配置接口
 * @property url      - 请求地址（必填）
 * @property method   - 请求方法，默认 GET
 * @property data     - 请求体（POST/PUT/PATCH 时使用）
 * @property params   - URL 查询参数（拼接到 ?key=value）
 */
export interface RequestConfig<T = unknown> {
  url: string
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
  data?: T
  params?: Record<string, unknown>
  headers?: Record<string, string>
  auth?: { username: string; password: string } // HTTP Basic Auth
}

/**
 * 通用请求函数
 * @typeParam R - 响应 data 的类型（后端返回的 data 字段类型）
 * @typeParam T - 请求参数的类型（data 的类型）
 * @param config - 请求配置对象
 * @returns Promise，resolve 后直接拿到 ApiResponse<R>
 *
 * @example
 * // GET 请求
 * request<UserInfo>({ url: '/user/1' })
 *
 * // POST 请求
 * request<{ token: string }, LoginParams>({
 *   url: '/login',
 *   method: 'POST',
 *   data: { username: 'admin', password: '123' }
 * })
 */
export function request<R = unknown, T = unknown>(config: RequestConfig<T>): Promise<ApiResponse<R>> {
  return service.request<ApiResponse<R>>({
    url: config.url,
    method: config.method || 'GET',
    data: config.data,
    params: config.params,
    headers: config.headers,
    auth: config.auth,
  }) as unknown as Promise<ApiResponse<R>>
}

// 同时导出 axios 实例，供需要直接使用 axios 原生方法的场景（如文件上传）
export default service
