import { ApiError } from '@/types'

const API_BASE = import.meta.env.VITE_API_BASE || ''

function toCamelCase(str: string): string {
  return str.replace(/_([a-z])/g, (_, letter: string) => letter.toUpperCase())
}

function toSnakeCase(str: string): string {
  return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
}

function transformKeys<T>(obj: unknown, transformer: (key: string) => string): T {
  if (Array.isArray(obj)) {
    return obj.map((item) => transformKeys(item, transformer)) as T
  }
  if (obj !== null && typeof obj === 'object') {
    const result: Record<string, unknown> = {}
    for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
      result[transformer(key)] = transformKeys(value, transformer)
    }
    return result as T
  }
  return obj as T
}

export function fromApi<T>(data: unknown): T {
  return transformKeys<T>(data, toCamelCase)
}

export function toApi<T>(data: unknown): T {
  return transformKeys<T>(data, toSnakeCase)
}

export async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
  const url = `${API_BASE}${path}`
  const options: RequestInit = {
    method,
    headers: { 'Content-Type': 'application/json' },
  }

  if (body !== undefined) {
    options.body = JSON.stringify(toApi(body))
  }

  const response = await fetch(url, options)

  if (!response.ok) {
    let message = `请求失败 (${response.status})`
    let code: string | undefined
    try {
      const errorData = await response.json()
      if (typeof errorData === 'object' && errorData !== null) {
        // 提取结构化错误响应
        if ('error' in errorData && typeof errorData.error === 'string') {
          message = errorData.error
        } else if ('message' in errorData && typeof errorData.message === 'string') {
          message = errorData.message
        }
        // 提取错误码
        if ('code' in errorData && typeof errorData.code === 'string') {
          code = errorData.code
        }
      }
    } catch {
      // ignore JSON parse error
    }
    throw new ApiError(response.status, message, code)
  }

  if (response.status === 204) {
    return undefined as T
  }

  const data = await response.json()
  return fromApi<T>(data)
}
