import type { League } from '@gridpilot/racing/domain/entities/League'; import type { IAllLeaguesWithCapacityPresenter, LeagueWithCapacityViewModel, AllLeaguesWithCapacityViewModel, } from '@gridpilot/racing/application/presenters/IAllLeaguesWithCapacityPresenter'; export class AllLeaguesWithCapacityPresenter implements IAllLeaguesWithCapacityPresenter { private viewModel: AllLeaguesWithCapacityViewModel | null = null; present( leagues: League[], memberCounts: Map ): AllLeaguesWithCapacityViewModel { const leagueItems: LeagueWithCapacityViewModel[] = leagues.map((league) => { const usedSlots = memberCounts.get(league.id) ?? 0; // Ensure we never expose an impossible state like 26/24: // clamp maxDrivers to at least usedSlots at the application boundary. const configuredMax = league.settings.maxDrivers ?? usedSlots; const safeMaxDrivers = Math.max(configuredMax, usedSlots); const base: LeagueWithCapacityViewModel = { id: league.id, name: league.name, description: league.description, ownerId: league.ownerId, settings: { ...league.settings, maxDrivers: safeMaxDrivers, }, createdAt: league.createdAt.toISOString(), usedSlots, }; if (!league.socialLinks) { return base; } const socialLinks: NonNullable = {}; if (league.socialLinks.discordUrl) { socialLinks.discordUrl = league.socialLinks.discordUrl; } if (league.socialLinks.youtubeUrl) { socialLinks.youtubeUrl = league.socialLinks.youtubeUrl; } if (league.socialLinks.websiteUrl) { socialLinks.websiteUrl = league.socialLinks.websiteUrl; } if (Object.keys(socialLinks).length === 0) { return base; } return { ...base, socialLinks, }; }); this.viewModel = { leagues: leagueItems, totalCount: leagueItems.length, }; return this.viewModel; } getViewModel(): AllLeaguesWithCapacityViewModel { if (!this.viewModel) { throw new Error('Presenter has not been called yet'); } return this.viewModel; } }