import type { ApiData, ApiErrorBody, FileDto, FolderDto, ListFilesParams, OcrJob, } from './types'; const API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL ?? 'http://localhost:8080/api/v1'; export const DEFAULT_TIMEOUT_MS = 15_000; type QueryParams = Record; function toQuery(params?: QueryParams): string { if ( !params ) return ''; const search = new URLSearchParams(); for ( const [key, value] of Object.entries(params) ) { if ( value === undefined || value === null ) continue; search.set(key, String(value)); } const query = search.toString(); return query ? `?${query}` : ''; } export class ApiError extends Error { readonly code: string; constructor(code: string, message: string) { super(message); this.name = 'ApiError'; this.code = code; } } async function request( path: string, init: RequestInit = {}, timeoutMs: number = DEFAULT_TIMEOUT_MS ): Promise> { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { let response: Response; try { response = await fetch(`${API_BASE_URL}${path}`, { ...init, signal: controller.signal, }); } catch { throw new ApiError('NETWORK_ERROR', 'Serveur injoignable'); } const body = (await response.json().catch(() => null)) as | ApiData | ApiErrorBody | null; if ( !response.ok ) { const error = body != null && 'error' in body ? body.error : null; throw new ApiError( error?.code ?? `HTTP_${response.status}`, error?.message ?? response.statusText ); } return (body ?? { data: undefined as T }) as ApiData; } finally { clearTimeout(timer); } } export const api = { getApiBaseUrl: () => API_BASE_URL, health: () => request<{ status: string }>('/health'), listFiles: (params?: ListFilesParams) => request(`/files${toQuery(params)}`), getFile: (id: string) => request(`/files/${encodeURIComponent(id)}`), deleteFile: (id: string) => request<{ id: string }>(`/files/${encodeURIComponent(id)}`, { method: 'DELETE', }), searchFiles: (q: string, params?: QueryParams) => request(`/files/search${toQuery({ q, ...params })}`), listFolders: () => request('/files/folders'), uploadFile: ( file: { uri: string; name: string; mimeType: string }, folderId?: string | null ) => { const form = new FormData(); form.append('file', { uri: file.uri, name: file.name, type: file.mimeType, } as unknown as Blob); if ( folderId ) form.append('folderId', folderId); return request('/files/upload', { method: 'POST', body: form, }); }, createOcrJob: (fileId: string) => request('/ocr/jobs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileId }), }), getOcrJob: (id: string) => request(`/ocr/jobs/${encodeURIComponent(id)}`), };