import type { AuthResponse, RegisterRequest, LoginRequest, User, Server, ServerWithChannelsAndMembers, Channel, MessageWithUser, MemberWithUser, Attachment, DmChannel, DmMessageWithUser, CreateServerRequest, UpdateServerRequest, CreateChannelRequest, UpdateChannelRequest, CreateMessageRequest, UpdateMessageRequest, UpdateUserRequest, JoinServerRequest, UpdateMemberRequest, LiveKitTokenResponse, CreateDmRequest, CreateDmMessageRequest, Friend, FriendRequest, } from '@opencord/shared'; const BASE_URL = '/api'; function getToken(): string | null { return localStorage.getItem('opencord_token'); } async function request( method: string, path: string, body?: unknown, requireAuth = true, ): Promise { const headers: Record = {}; if (body) { headers['Content-Type'] = 'application/json'; } if (requireAuth) { const token = getToken(); if (token) { headers['Authorization'] = `Bearer ${token}`; } } const response = await fetch(`${BASE_URL}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined, }); if (!response.ok) { const error = await response.json().catch(() => ({ error: 'Request failed' })); throw new Error((error as { error: string }).error || `HTTP ${response.status}`); } return response.json() as Promise; } async function uploadFile(file: File): Promise { const formData = new FormData(); formData.append('file', file); const token = getToken(); const headers: Record = {}; if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(`${BASE_URL}/uploads`, { method: 'POST', headers, body: formData, }); if (!response.ok) { const error = await response.json().catch(() => ({ error: 'Upload failed' })); throw new Error((error as { error: string }).error || `HTTP ${response.status}`); } return response.json() as Promise; } export const api = { auth: { register: (data: RegisterRequest) => request('POST', '/auth/register', data, false), login: (data: LoginRequest) => request('POST', '/auth/login', data, false), }, users: { me: () => request('GET', '/users/@me'), update: (data: UpdateUserRequest) => request('PATCH', '/users/@me', data), get: (id: string) => request('GET', `/users/${id}`), }, servers: { list: () => request('GET', '/servers'), get: (id: string) => request('GET', `/servers/${id}`), create: (data: CreateServerRequest) => request('POST', '/servers', data), update: (id: string, data: UpdateServerRequest) => request('PATCH', `/servers/${id}`, data), delete: (id: string) => request<{ success: boolean }>('DELETE', `/servers/${id}`), invite: (id: string) => request<{ inviteCode: string }>('POST', `/servers/${id}/invite`), join: (id: string, data: JoinServerRequest) => request('POST', `/servers/${id}/join`, data), joinByCode: (inviteCode: string) => request('POST', '/servers/join', { inviteCode }), members: (id: string) => request('GET', `/servers/${id}/members`), updateMember: (serverId: string, userId: string, data: UpdateMemberRequest) => request('PATCH', `/servers/${serverId}/members/${userId}`, data), removeMember: (serverId: string, userId: string) => request<{ success: boolean }>('DELETE', `/servers/${serverId}/members/${userId}`), }, channels: { list: (serverId: string) => request('GET', `/servers/${serverId}/channels`), create: (serverId: string, data: CreateChannelRequest) => request('POST', `/servers/${serverId}/channels`, data), update: (id: string, data: UpdateChannelRequest) => request('PATCH', `/channels/${id}`, data), delete: (id: string) => request<{ success: boolean }>('DELETE', `/channels/${id}`), messages: (id: string, before?: string, limit = 50) => { const params = new URLSearchParams(); if (before) params.set('before', before); params.set('limit', String(limit)); return request('GET', `/channels/${id}/messages?${params}`); }, sendMessage: (channelId: string, data: CreateMessageRequest) => request('POST', `/channels/${channelId}/messages`, data), }, messages: { update: (id: string, data: UpdateMessageRequest) => request('PATCH', `/messages/${id}`, data), delete: (id: string) => request<{ success: boolean }>('DELETE', `/messages/${id}`), }, uploads: { upload: uploadFile, url: (filename: string) => `${BASE_URL}/uploads/${filename}`, }, dm: { list: () => request('GET', '/dm'), create: (data: CreateDmRequest) => request('POST', '/dm', data), close: (id: string) => request<{ success: boolean }>('DELETE', `/dm/${id}`), messages: (id: string, before?: string, limit = 50) => { const params = new URLSearchParams(); if (before) params.set('before', before); params.set('limit', String(limit)); return request('GET', `/dm/${id}/messages?${params}`); }, sendMessage: (id: string, data: CreateDmMessageRequest) => request('POST', `/dm/${id}/messages`, data), updateMessage: (id: string, data: UpdateMessageRequest) => request('PATCH', `/dm/messages/${id}`, data), deleteMessage: (id: string) => request<{ success: boolean }>('DELETE', `/dm/messages/${id}`), addMember: (dmChannelId: string, data: { userId: string }) => request('POST', `/dm/${dmChannelId}/members`, data), leave: (dmChannelId: string) => request<{ success: boolean }>('DELETE', `/dm/${dmChannelId}/members`), }, social: { friends: () => request('GET', '/social/friends'), requests: () => request('GET', '/social/requests'), sendRequest: (username: string) => request<{ success: boolean }>('POST', '/social/requests', { username }), updateRequest: (id: string, status: 'accepted' | 'declined') => request<{ success: boolean }>('PATCH', `/social/requests/${id}`, { status }), removeFriend: (id: string) => request<{ success: boolean }>('DELETE', `/social/friends/${id}`), cancelRequest: (id: string) => request<{ success: boolean }>('DELETE', `/social/requests/${id}`), search: (q: string) => request('GET', `/social/search?q=${encodeURIComponent(q)}`), }, livekit: { token: (channelId: string) => request('POST', '/livekit/token', { channelId }), dmToken: (dmChannelId: string) => request('POST', '/livekit/token', { dmChannelId }), }, };