import { ApiError } from '@/types/api'

const DEFAULT_TIMEOUT = 15000

// 允许外部注入 token 获取函数，避免与 store 循环依赖
let tokenGetter: (() => string | null) | null = null
export function setTokenGetter(fn: () => string | null) {
  tokenGetter = fn
}

interface RequestOptions extends RequestInit {
  params?: Record<string, unknown>
  timeout?: number
}

// 运行期配置优先（容器启动时由 nginx entrypoint 注入到 /config.js 写入 window.__APP_CONFIG__）
// 构建期 VITE_API_BASE_URL 作为本地 dev 的 fallback
function resolveApiBaseUrl(): string {
  if (typeof window !== 'undefined') {
    const runtime = (window as unknown as { __APP_CONFIG__?: { apiBaseUrl?: string } }).__APP_CONFIG__
    const url = runtime?.apiBaseUrl
    // 占位符未替换时（如本地直接打开 public/config.js）忽略
    if (url && !(url.startsWith('__') && url.endsWith('__'))) {
      return url
    }
  }
  return import.meta.env.VITE_API_BASE_URL || ''
}

const API_BASE_URL = resolveApiBaseUrl()

async function request<T = unknown>(url: string, options: RequestOptions = {}): Promise<T> {
  url = `${API_BASE_URL}${url}`
  const token = tokenGetter?.() ?? localStorage.getItem('access_token')

  const headers: Record<string, string> = {
    'Content-Type': 'application/json',
    ...(options.headers as Record<string, string>),
  }

  if (token) {
    headers['Authorization'] = `Bearer ${token}`
  }

  // 拼接查询参数
  if (options.params) {
    const query = new URLSearchParams(
      Object.entries(options.params)
        .filter(([, v]) => v !== undefined && v !== null && v !== '')
        .map(([k, v]) => [k, String(v)])
    )
    const separator = url.includes('?') ? '&' : '?'
    url = `${url}${separator}${query}`
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const { params: _params, timeout: _timeout, ...fetchOptions } = options

  // 超时控制
  const controller = new AbortController()
  const timeoutId = setTimeout(() => controller.abort(), options.timeout ?? DEFAULT_TIMEOUT)

  try {
    const response = await fetch(url, {
      ...fetchOptions,
      headers,
      signal: controller.signal,
    })

    // 401 未授权：登录/注册接口解析后端错误信息，其余接口重定向到登录页
    if (response.status === 401) {
      const isAuthEndpoint = /\/api\/v1\/(login|register)$/.test(url)
      if (!isAuthEndpoint) {
        localStorage.removeItem('access_token')
        localStorage.removeItem('refresh_token')
        localStorage.removeItem('user')
        const redirect = encodeURIComponent(
          window.location.pathname + window.location.search,
        )
        window.location.href = `/login?redirect=${redirect}`
        throw new ApiError('登录已过期，请重新登录', 401)
      }
      let msg = '用户名或密码错误'
      try {
        const body = await response.json()
        if (body.message) msg = body.message
      } catch { /* ignore parse error */ }
      throw new ApiError(msg, 401)
    }

    // 防护非 JSON 响应（如网关 HTML 错误页）
    let result
    try {
      result = await response.json()
    } catch {
      throw new ApiError('服务器返回了非预期的响应格式', response.status)
    }

    // 业务错误
    if (result.code !== 0) {
      throw new ApiError(result.message || '请求失败', result.code)
    }

    return result.data
  } catch (error) {
    if (error instanceof ApiError) throw error
    if (error instanceof DOMException && error.name === 'AbortError') {
      throw new ApiError('请求超时，请检查网络连接', 0)
    }
    throw error
  } finally {
    clearTimeout(timeoutId)
  }
}

export const api = {
  get: <T = unknown>(url: string, params?: Record<string, unknown>) =>
    request<T>(url, { method: 'GET', params }),

  post: <T = unknown>(url: string, data?: unknown) =>
    request<T>(url, { method: 'POST', body: JSON.stringify(data) }),

  put: <T = unknown>(url: string, data?: unknown) =>
    request<T>(url, { method: 'PUT', body: JSON.stringify(data) }),

  patch: <T = unknown>(url: string, data?: unknown) =>
    request<T>(url, { method: 'PATCH', body: JSON.stringify(data) }),

  delete: <T = unknown>(url: string) =>
    request<T>(url, { method: 'DELETE' }),
}
