import type { AttachmentKind, AttachmentUploadResponse, CallTokenResponse, ChannelResponse, ChannelType, FriendshipResponse, MessageResponse, ServerResponse, UserResponse, } from '../types/api' const API_BASE_URL = import.meta.env.VITE_API_BASE_URL as string export class ApiError extends Error { status: number constructor(status: number, message: string) { super(message) this.status = status } } export interface MessageAttachmentInput { key: string fileName: string contentType: string kind: AttachmentKind } export function createApiClient(getAccessToken: () => string | undefined) { async function request(path: string, init?: RequestInit): Promise { const token = getAccessToken() const response = await fetch(`${API_BASE_URL}${path}`, { ...init, headers: { ...(init?.body ? { 'Content-Type': 'application/json' } : {}), ...(token ? { Authorization: `Bearer ${token}` } : {}), ...init?.headers, }, }) if (!response.ok) { const body = await response.text().catch(() => '') throw new ApiError(response.status, body || response.statusText) } if (response.status === 204) { return undefined as T } return (await response.json()) as T } return { getCurrentUser: () => request('/users/me'), getUserById: (id: string) => request(`/users/${id}`), searchUsers: (query: string) => request(`/users/search?query=${encodeURIComponent(query)}`), getFriends: () => request('/friends'), getPendingFriendRequests: () => request('/friends/requests'), sendFriendRequest: (addresseeId: string) => request('/friends/requests', { method: 'POST', body: JSON.stringify({ addresseeId }) }), acceptFriendRequest: (id: string) => request(`/friends/requests/${id}/accept`, { method: 'POST' }), rejectFriendRequest: (id: string) => request(`/friends/requests/${id}/reject`, { method: 'POST' }), removeFriend: (id: string) => request(`/friends/${id}`, { method: 'DELETE' }), blockUser: (userId: string) => request(`/friends/block/${userId}`, { method: 'POST' }), getServers: () => request('/servers'), createServer: (name: string) => request('/servers', { method: 'POST', body: JSON.stringify({ name }) }), getServerChannels: (serverId: string) => request(`/servers/${serverId}/channels`), createChannel: (serverId: string, name: string, type: ChannelType) => request(`/servers/${serverId}/channels`, { method: 'POST', body: JSON.stringify({ name, type }) }), getOrCreateDirectMessageChannel: (otherUserId: string) => request(`/channels/dm/${otherUserId}`, { method: 'POST' }), getChannelMessages: (channelId: string, before?: string, take = 50) => request( `/channels/${channelId}/messages?take=${take}${before ? `&before=${before}` : ''}`, ), sendMessage: (channelId: string, content: string, attachments: MessageAttachmentInput[] = [], replyToMessageId?: string) => request(`/channels/${channelId}/messages`, { method: 'POST', body: JSON.stringify({ content, replyToMessageId, attachments }), }), editMessage: (id: string, content: string) => request(`/messages/${id}`, { method: 'PATCH', body: JSON.stringify({ content }) }), deleteMessage: (id: string) => request(`/messages/${id}`, { method: 'DELETE' }), requestAttachmentUploadUrl: (fileName: string, contentType: string) => request('/attachments/upload-url', { method: 'POST', body: JSON.stringify({ fileName, contentType }), }), requestCallToken: (channelId: string) => request(`/channels/${channelId}/call-token`, { method: 'POST' }), } } export async function uploadFileToPresignedUrl(uploadUrl: string, file: File | Blob, contentType: string): Promise { const response = await fetch(uploadUrl, { method: 'PUT', headers: { 'Content-Type': contentType }, body: file, }) if (!response.ok) { throw new ApiError(response.status, `Failed to upload file: ${response.statusText}`) } } export type ApiClient = ReturnType