/**
 * Standardized API fetch wrapper with consistent error handling.
 *
 * Usage:
 *   const data = await apiFetch<{ projects: Project[] }>('/api/projects')
 *   const result = await apiFetch('/api/admin/settings', { method: 'PUT', body: JSON.stringify({ ... }) })
 */

export class ApiError extends Error {
  status: number
  data: any

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

interface ApiFetchOptions extends RequestInit {
  /** If true, don't throw on non-2xx responses — return null instead */
  silent?: boolean
}

/**
 * Typed fetch wrapper that:
 * - Automatically sets Content-Type for JSON bodies
 * - Parses JSON responses
 * - Throws ApiError with status code and server error message on failure
 * - Supports silent mode for optional/non-critical requests
 */
export async function apiFetch<T = any>(
  url: string,
  options: ApiFetchOptions = {}
): Promise<T> {
  const { silent, ...fetchOptions } = options

  // Auto-set Content-Type for JSON string bodies
  if (
    fetchOptions.body &&
    typeof fetchOptions.body === 'string' &&
    !fetchOptions.headers
  ) {
    fetchOptions.headers = { 'Content-Type': 'application/json' }
  } else if (
    fetchOptions.body &&
    typeof fetchOptions.body === 'string' &&
    fetchOptions.headers &&
    !(fetchOptions.headers as Record<string, string>)['Content-Type']
  ) {
    fetchOptions.headers = {
      ...fetchOptions.headers,
      'Content-Type': 'application/json',
    }
  }

  const res = await fetch(url, fetchOptions)

  if (!res.ok) {
    if (silent) return null as T

    let errorData: any = null
    let errorMessage = `HTTP ${res.status}`
    try {
      errorData = await res.json()
      errorMessage = errorData?.error || errorData?.message || errorMessage
    } catch {
      // Response not JSON, use status text
      errorMessage = res.statusText || errorMessage
    }
    throw new ApiError(errorMessage, res.status, errorData)
  }

  // Handle 204 No Content
  if (res.status === 204) return null as T

  try {
    return await res.json()
  } catch {
    return null as T
  }
}
