import type { AnalysisResult, ComparisonResult, FilterParams, JobStatus } from '../types'; const API_BASE = '/api/v1'; class ApiError extends Error { constructor( public status: number, message: string, public correlationId?: string, ) { super(message); this.name = 'ApiError'; } } function getHeaders(): Record { const apiKey = localStorage.getItem('api_key') || 'dev-key-1'; return { 'X-API-Key': apiKey, 'Content-Type': 'application/json', }; } async function handleResponse(response: Response): Promise { if (!response.ok) { const body = await response.json().catch(() => ({ detail: response.statusText })); throw new ApiError(response.status, body.detail || 'Request failed', body.correlation_id); } return response.json(); } export const api = { async uploadFile(file: File, source?: string): Promise { const formData = new FormData(); formData.append('file', file); const params = new URLSearchParams(); if (source) params.set('source', source); const apiKey = localStorage.getItem('api_key') || 'dev-key-1'; const response = await fetch(`${API_BASE}/upload?${params}`, { method: 'POST', headers: { 'X-API-Key': apiKey }, body: formData, }); return handleResponse(response); }, async uploadChunked( file: File, onProgress?: (progress: number) => void, ): Promise { const CHUNK_SIZE = 10 * 1024 * 1024; // 10MB const totalChunks = Math.ceil(file.size / CHUNK_SIZE); let uploadId: string | undefined; let lastStatus: JobStatus | undefined; for (let i = 0; i < totalChunks; i++) { const start = i * CHUNK_SIZE; const end = Math.min(start + CHUNK_SIZE, file.size); const chunk = file.slice(start, end); const formData = new FormData(); formData.append('file', chunk, file.name); const params = new URLSearchParams({ chunk_index: String(i), total_chunks: String(totalChunks), }); if (uploadId) params.set('upload_id', uploadId); const apiKey = localStorage.getItem('api_key') || 'dev-key-1'; const response = await fetch(`${API_BASE}/upload/chunked?${params}`, { method: 'POST', headers: { 'X-API-Key': apiKey }, body: formData, }); lastStatus = await handleResponse(response); uploadId = lastStatus.job_id; onProgress?.((i + 1) / totalChunks); } return lastStatus!; }, async getJobs(): Promise { const response = await fetch(`${API_BASE}/jobs`, { headers: getHeaders() }); return handleResponse(response); }, async getJobResult(jobId: string): Promise { const response = await fetch(`${API_BASE}/jobs/${jobId}`, { headers: getHeaders() }); return handleResponse(response); }, async getJobStatus(jobId: string): Promise { const response = await fetch(`${API_BASE}/jobs/${jobId}/status`, { headers: getHeaders() }); return handleResponse(response); }, async filterResults(jobId: string, filters: FilterParams) { const response = await fetch(`${API_BASE}/jobs/${jobId}/filter`, { method: 'POST', headers: getHeaders(), body: JSON.stringify(filters), }); return handleResponse<{ total: number; page: number; entries: AnalysisResult['entries'] }>(response); }, async compareSegments(jobId: string, segmentA: FilterParams, segmentB: FilterParams): Promise { const response = await fetch(`${API_BASE}/jobs/${jobId}/compare`, { method: 'POST', headers: getHeaders(), body: JSON.stringify({ segment_a: segmentA, segment_b: segmentB }), }); return handleResponse(response); }, async exportResults(jobId: string, format: 'csv' | 'json' | 'pdf', filters?: FilterParams): Promise { const response = await fetch(`${API_BASE}/jobs/${jobId}/export?fmt=${format}`, { method: 'POST', headers: getHeaders(), body: filters ? JSON.stringify(filters) : '{}', }); if (!response.ok) { throw new ApiError(response.status, 'Export failed'); } return response.blob(); }, subscribeToEvents(onMessage: (data: Record) => void): EventSource { const apiKey = localStorage.getItem('api_key') || 'dev-key-1'; const es = new EventSource(`${API_BASE}/events/analysis?api_key=${apiKey}`); es.addEventListener('analysis_update', (event) => { try { const data = JSON.parse(event.data); onMessage(data); } catch { // ignore parse errors } }); return es; }, };