"use client"; const API_URL = process.env.NEXT_PUBLIC_API_URL || ""; const REQUEST_TIMEOUT_MS = 35000; const MAX_RETRIES = 3; class ApiError extends Error { status: number; constructor(message: string, status: number) { super(message); this.status = status; this.name = "ApiError"; } } async function fetchWithTimeout( url: string, options: RequestInit, timeout: number, ): Promise { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { ...options, signal: controller.signal, }); return response; } finally { clearTimeout(timeoutId); } } export async function apiRequest( endpoint: string, options: RequestInit = {}, retries = 0, ): Promise { const token = typeof window !== "undefined" ? localStorage.getItem("token") : null; if (!API_URL) { throw new ApiError("API URL not configured", 500); } const headers: HeadersInit = { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}), ...options.headers, }; try { const res = await fetchWithTimeout( `${API_URL}${endpoint}`, { ...options, headers }, REQUEST_TIMEOUT_MS, ); if (res.status === 401 || res.status === 403) { if (typeof window !== "undefined") { localStorage.removeItem("token"); localStorage.removeItem("user"); window.location.href = "/signin"; } throw new ApiError("Session expired. Please sign in again.", res.status); } if (res.status >= 500 && retries < MAX_RETRIES) { await new Promise((r) => setTimeout(r, 1000 * (retries + 1))); return apiRequest(endpoint, options, retries + 1); } if (!res.ok) { const data = await res.json().catch(() => ({})); throw new ApiError(data.detail || "Request failed", res.status); } if (res.status === 204) { return {} as T; } return res.json(); } catch (error) { if (error instanceof ApiError) throw error; if (error instanceof Error) { if (error.name === "AbortError") { throw new ApiError("Request timed out. Please try again.", 408); } if (retries < MAX_RETRIES) { await new Promise((r) => setTimeout(r, 1000 * (retries + 1))); return apiRequest(endpoint, options, retries + 1); } } throw new ApiError("Network error. Please check your connection.", 0); } } export async function apiGet(endpoint: string): Promise { return apiRequest(endpoint, { method: "GET" }); } export async function apiPost(endpoint: string, body?: unknown): Promise { return apiRequest(endpoint, { method: "POST", body: body ? JSON.stringify(body) : undefined, }); } export async function apiPatch( endpoint: string, body?: unknown, ): Promise { return apiRequest(endpoint, { method: "PATCH", body: body ? JSON.stringify(body) : undefined, }); } export async function apiDelete(endpoint: string): Promise { await apiRequest(endpoint, { method: "DELETE" }); } export { ApiError };