import type { CreateOrderRequest, ForgotPasswordRequest, LoginRequest, LoginResponse, Order, OrderStatusUpdateRequest, Product, RegisterRequest } from '../types/domain'; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? ''; export class ApiError extends Error { constructor( message: string, public readonly status: number, public readonly payload?: unknown ) { super(message); this.name = 'ApiError'; } } interface RequestOptions extends Omit { token?: string | null; body?: unknown; } function url(path: string) { return `${API_BASE_URL}${path}`; } async function parseResponse(response: Response) { const contentType = response.headers.get('content-type') ?? ''; if (response.status === 204) { return undefined; } if (contentType.includes('application/json')) { return response.json(); } return response.text(); } async function request(path: string, options: RequestOptions = {}): Promise { const headers = new Headers(options.headers); if (options.body !== undefined && !headers.has('Content-Type')) { headers.set('Content-Type', 'application/json'); } if (options.token) { headers.set('Authorization', `Bearer ${options.token}`); } const response = await fetch(url(path), { ...options, headers, body: options.body !== undefined ? JSON.stringify(options.body) : undefined }); const payload = await parseResponse(response); if (!response.ok) { const message = typeof payload === 'string' ? payload : payload && typeof payload === 'object' && 'message' in payload ? String((payload as { message: unknown }).message) : `Request failed with status ${response.status}`; throw new ApiError(message, response.status, payload); } return payload as T; } export const api = { login: (body: LoginRequest) => request('/auth/login', { method: 'POST', body }), register: (body: RegisterRequest) => request('/auth/register', { method: 'POST', body }), forgotPassword: (body: ForgotPasswordRequest) => request('/auth/forgot-password', { method: 'POST', body }), getProducts: (token: string, activeOnly = false) => request(activeOnly ? '/products/active' : '/products', { token }), createProduct: (token: string, body: Product) => request('/products', { method: 'POST', token, body }), updateProduct: (token: string, pid: number, body: Product) => request(`/products/${pid}`, { method: 'PUT', token, body }), deleteProduct: (token: string, pid: number) => request(`/products/${pid}`, { method: 'DELETE', token }), addStock: (token: string, pid: number, quantity: number, async = false) => request(`/products/${pid}/add-stock${async ? '/async' : ''}`, { method: 'PATCH', token, body: { quantity } }), reserveStock: (token: string, pid: number, quantity: number, async = false) => request(`/products/${pid}/reserve-stock${async ? '/async' : ''}`, { method: 'PATCH', token, body: { quantity } }), createOrder: (token: string, body: CreateOrderRequest) => request('/orders', { method: 'POST', token, body }), getMyOrders: (token: string) => request('/orders/mine', { token }), getAllOrders: (token: string) => request('/orders', { token }), updateOrderStatus: (token: string, orderId: number, body: OrderStatusUpdateRequest) => request(`/orders/${orderId}/status`, { method: 'PATCH', token, body }) }; export function streamUrl(admin: boolean) { return url(admin ? '/orders/stream' : '/orders/stream/mine'); }