export interface ApiResponse {
  code: number
  msg: string
  [key: string]: unknown
}

export class ApiError extends Error {
  readonly code: number

  constructor(message: string, code = 500) {
    super(message)
    this.name = 'ApiError'
    this.code = code
  }
}

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/dev-api'
const AUTH_TOKEN_KEY = 'battery_detection_auth_token'
const AUTH_USER_KEY = 'battery_detection_auth_user'

type UnauthorizedHandler = () => void | Promise<void>

let unauthorizedHandler: UnauthorizedHandler | undefined
let unauthorizedHandling: Promise<void> | null = null

export function setUnauthorizedHandler(handler: UnauthorizedHandler) {
  unauthorizedHandler = handler
}

function clearInvalidSession() {
  localStorage.removeItem(AUTH_TOKEN_KEY)
  localStorage.removeItem(AUTH_USER_KEY)
}

async function handleUnauthorized() {
  clearInvalidSession()
  if (!unauthorizedHandler) return

  if (!unauthorizedHandling) {
    unauthorizedHandling = Promise.resolve(unauthorizedHandler())
      .finally(() => {
        unauthorizedHandling = null
      })
  }

  await unauthorizedHandling
}

export async function request<T extends ApiResponse>(path: string, init: RequestInit = {}): Promise<T> {
  const headers = new Headers(init.headers)
  const token = localStorage.getItem(AUTH_TOKEN_KEY)

  if (init.body && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json')
  }
  if (token) {
    headers.set('Authorization', `Bearer ${token}`)
  }

  let response: Response
  try {
    response = await fetch(`${API_BASE_URL}${path}`, { ...init, headers })
  } catch {
    throw new ApiError('无法连接服务器，请检查后端服务是否已启动', 0)
  }

  let result: T
  try {
    result = await response.json() as T
  } catch {
    throw new ApiError(`服务器响应异常（HTTP ${response.status}）`, response.status)
  }

  if (response.status === 401 || result.code === 401) {
    await handleUnauthorized()
  }

  if (!response.ok || result.code !== 200) {
    throw new ApiError(result.msg || `请求失败（HTTP ${response.status}）`, result.code || response.status)
  }

  return result
}
