import { ApiClient } from '@/lib/api'; import type { LeaguesApiClient } from '@/lib/api/leagues/LeaguesApiClient'; import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl'; import { LeagueMemberViewModel } from '@/lib/view-models/LeagueMemberViewModel'; import type { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO'; import type { LeagueMembership } from '@/lib/types/LeagueMembership'; let cachedLeaguesApiClient: LeaguesApiClient | undefined; function getDefaultLeaguesApiClient(): LeaguesApiClient { if (cachedLeaguesApiClient) return cachedLeaguesApiClient; const api = new ApiClient(getWebsiteApiBaseUrl()); cachedLeaguesApiClient = (api as any).leagues as LeaguesApiClient; return cachedLeaguesApiClient; } export class LeagueMembershipService { // In-memory cache for memberships (populated via API calls) private static leagueMemberships = new Map(); constructor(private readonly leaguesApiClient?: LeaguesApiClient) {} private getClient(): LeaguesApiClient { return this.leaguesApiClient ?? getDefaultLeaguesApiClient(); } async getLeagueMemberships(leagueId: string, currentUserId: string): Promise { const dto = await this.getClient().getMemberships(leagueId); const members: LeagueMemberDTO[] = ((dto as any)?.members ?? (dto as any)?.memberships ?? []) as LeagueMemberDTO[]; return members.map((m) => new LeagueMemberViewModel(m, currentUserId)); } async removeMember(leagueId: string, performerDriverId: string, targetDriverId: string): Promise<{ success: boolean }> { return this.getClient().removeMember(leagueId, performerDriverId, targetDriverId) as unknown as { success: boolean }; } /** * Get a specific membership from cache. */ static getMembership(leagueId: string, driverId: string): LeagueMembership | null { const list = this.leagueMemberships.get(leagueId); if (!list) return null; return list.find((m) => m.driverId === driverId) ?? null; } /** * Get all members of a league from cache. */ static getLeagueMembers(leagueId: string): LeagueMembership[] { return [...(this.leagueMemberships.get(leagueId) ?? [])]; } /** * Fetch and cache memberships for a league via API. */ static async fetchLeagueMemberships(leagueId: string): Promise { try { const result = await getDefaultLeaguesApiClient().getMemberships(leagueId); const memberships: LeagueMembership[] = ((result as any)?.members ?? []).map((member: any) => ({ id: `${member.driverId}-${leagueId}`, // Generate ID since API doesn't provide it leagueId, driverId: member.driverId, role: member.role, status: 'active', // Assume active since API returns current members joinedAt: member.joinedAt, })); this.setLeagueMemberships(leagueId, memberships); return memberships; } catch (error) { console.error('Failed to fetch league memberships:', error); return []; } } /** * Join a league. * * NOTE: The API currently exposes membership mutations via league member management endpoints. * For now we keep the website decoupled by consuming only the API through this service. */ async joinLeague(leagueId: string, driverId: string): Promise { // Temporary: no join endpoint exposed yet in API. // Keep behavior predictable for UI. throw new Error('Joining leagues is not available in this build.'); } /** * Leave a league. */ async leaveLeague(leagueId: string, driverId: string): Promise { // Temporary: no leave endpoint exposed yet in API. throw new Error('Leaving leagues is not available in this build.'); } /** * Set memberships in cache (for use after API calls). */ static setLeagueMemberships(leagueId: string, memberships: LeagueMembership[]): void { this.leagueMemberships.set(leagueId, memberships); } /** * Clear cached memberships for a league. */ static clearLeagueMemberships(leagueId: string): void { this.leagueMemberships.delete(leagueId); } /** * Get iterator for cached memberships (for utility functions). */ static getCachedMembershipsIterator(): IterableIterator<[string, LeagueMembership[]]> { return this.leagueMemberships.entries(); } /** * Get all memberships for a specific driver across all leagues. */ static getAllMembershipsForDriver(driverId: string): LeagueMembership[] { const allMemberships: LeagueMembership[] = []; for (const [leagueId, memberships] of this.leagueMemberships.entries()) { const driverMembership = memberships.find((m) => m.driverId === driverId); if (driverMembership) { allMemberships.push(driverMembership); } } return allMemberships; } // Instance methods that delegate to static methods for consistency with service pattern /** * Get a specific membership from cache. */ getMembership(leagueId: string, driverId: string): LeagueMembership | null { return LeagueMembershipService.getMembership(leagueId, driverId); } /** * Get all members of a league from cache. */ getLeagueMembers(leagueId: string): LeagueMembership[] { return LeagueMembershipService.getLeagueMembers(leagueId); } /** * Fetch and cache memberships for a league via API. */ async fetchLeagueMemberships(leagueId: string): Promise { return LeagueMembershipService.fetchLeagueMemberships(leagueId); } /** * Set memberships in cache (for use after API calls). */ setLeagueMemberships(leagueId: string, memberships: LeagueMembership[]): void { LeagueMembershipService.setLeagueMemberships(leagueId, memberships); } /** * Clear cached memberships for a league. */ clearLeagueMemberships(leagueId: string): void { LeagueMembershipService.clearLeagueMemberships(leagueId); } }