export type ApiSession = {
  baseUrl: string
  token: string
}

type RequestOptions = {
  method?: string
  body?: unknown
  token?: string
  responseType?: 'json' | 'void'
  signal?: AbortSignal
}

type ErrorEnvelope = {
  error: {
    code: string
    message: string
  }
}

export class ApiError extends Error {
  status: number
  code: string

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

let configuredBaseUrl = normalizeBaseUrl(import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:9150')

export function setApiBaseUrl(baseUrl: string) {
  configuredBaseUrl = normalizeBaseUrl(baseUrl)
}

export function getApiBaseUrl() {
  return configuredBaseUrl
}

export async function login(email: string, password: string): Promise<ApiSession> {
  const response = await apiRequest<{ token: string }>('/api/v1/auth/login', {
    method: 'POST',
    body: { email, password },
  })
  return { baseUrl: configuredBaseUrl, token: response.token }
}

export async function apiRequest<T>(path: string, options: RequestOptions = {}): Promise<T> {
  const isFormData = typeof FormData !== 'undefined' && options.body instanceof FormData
  const requestBody = options.body === undefined ? undefined : isFormData ? options.body : JSON.stringify(options.body)
  let response: Response
  try {
    response = await fetch(`${configuredBaseUrl}${path}`, {
      method: options.method ?? 'GET',
      headers: {
        ...(options.body !== undefined && !isFormData ? { 'Content-Type': 'application/json' } : {}),
        ...(options.token ? { Authorization: `Bearer ${options.token}` } : {}),
      },
      body: requestBody as BodyInit | undefined,
      signal: options.signal,
    })
  } catch {
    throw new ApiError(0, 'network_error', '无法连接服务器，请检查地址和网络后重试')
  }

  if (!response.ok) {
    let envelope: ErrorEnvelope | null = null
    try {
      const payload: unknown = await response.json()
      if (isErrorEnvelope(payload)) envelope = payload
    } catch {
      // 非 JSON 响应统一使用状态码文案，避免把服务端内部信息展示给用户。
    }
    if (envelope) throw new ApiError(response.status, envelope.error.code, envelope.error.message)
    throw new ApiError(response.status, fallbackErrorCode(response.status), fallbackErrorMessage(response.status))
  }

  if (response.status === 204 || options.responseType === 'void') {
    return undefined as T
  }

  let text: string
  try {
    text = await response.text()
  } catch {
    throw invalidResponse(response.status)
  }
  if (text.trim() === '') {
    throw invalidResponse(response.status)
  }
  try {
    return JSON.parse(text) as T
  } catch {
    throw invalidResponse(response.status)
  }
}

function invalidResponse(status: number) {
  return new ApiError(status, 'invalid_response', '服务器返回了无法识别的数据')
}

function isErrorEnvelope(value: unknown): value is ErrorEnvelope {
  if (!value || typeof value !== 'object' || !('error' in value)) return false
  const error = value.error
  return (
    !!error &&
    typeof error === 'object' &&
    'code' in error &&
    typeof error.code === 'string' &&
    error.code.trim() !== '' &&
    'message' in error &&
    typeof error.message === 'string' &&
    error.message.trim() !== ''
  )
}

function fallbackErrorCode(status: number) {
  if (status === 401) return 'unauthorized'
  if (status === 403) return 'forbidden'
  if (status === 404) return 'not_found'
  if (status === 409) return 'conflict'
  if (status >= 500) return 'internal_error'
  return 'request_failed'
}

function fallbackErrorMessage(status: number) {
  if (status === 401) return '登录已失效，请重新登录'
  if (status === 403) return '没有权限执行此操作'
  if (status === 404) return '请求的内容不存在'
  if (status === 409) return '操作冲突，请刷新后重试'
  if (status >= 500) return '服务器暂时无法处理请求，请稍后重试'
  return '请求失败，请稍后重试'
}

export function normalizeBaseUrl(value: string) {
  const trimmed = value.trim()
  return trimmed.replace(/\/+$/, '')
}
