import type { League } from '@gridpilot/racing/domain/entities/League'; import type { IAllLeaguesWithCapacityAndScoringPresenter, LeagueEnrichedData, LeagueSummaryViewModel, AllLeaguesWithCapacityAndScoringViewModel, } from '@gridpilot/racing/application/presenters/IAllLeaguesWithCapacityAndScoringPresenter'; export class AllLeaguesWithCapacityAndScoringPresenter implements IAllLeaguesWithCapacityAndScoringPresenter { private viewModel: AllLeaguesWithCapacityAndScoringViewModel | null = null; present(enrichedLeagues: LeagueEnrichedData[]): AllLeaguesWithCapacityAndScoringViewModel { const leagueItems: LeagueSummaryViewModel[] = enrichedLeagues.map((data) => { const { league, usedDriverSlots, season, scoringConfig, game, preset } = data; const configuredMaxDrivers = league.settings.maxDrivers ?? usedDriverSlots; const safeMaxDrivers = Math.max(configuredMaxDrivers, usedDriverSlots); const structureSummary = `Solo • ${safeMaxDrivers} drivers`; const qualifyingMinutes = 30; const mainRaceMinutes = typeof league.settings.sessionDuration === 'number' ? league.settings.sessionDuration : 40; const timingSummary = `${qualifyingMinutes} min Quali • ${mainRaceMinutes} min Race`; let scoringSummary: LeagueSummaryViewModel['scoring'] | undefined; let scoringPatternSummary: string | undefined; if (season && scoringConfig && game) { const dropPolicySummary = preset?.dropPolicySummary ?? this.deriveDropPolicySummary(scoringConfig); const primaryChampionshipType = preset?.primaryChampionshipType ?? (scoringConfig.championships[0]?.type ?? 'driver'); const scoringPresetName = preset?.name ?? 'Custom'; scoringPatternSummary = `${scoringPresetName} • ${dropPolicySummary}`; scoringSummary = { gameId: game.id, gameName: game.name, primaryChampionshipType, scoringPresetId: scoringConfig.scoringPresetId ?? 'custom', scoringPresetName, dropPolicySummary, scoringPatternSummary, }; } return { id: league.id, name: league.name, description: league.description, ownerId: league.ownerId, createdAt: league.createdAt, maxDrivers: safeMaxDrivers, usedDriverSlots, maxTeams: undefined, usedTeamSlots: undefined, structureSummary, scoringPatternSummary, timingSummary, scoring: scoringSummary, }; }); this.viewModel = { leagues: leagueItems, totalCount: leagueItems.length, }; return this.viewModel; } getViewModel(): AllLeaguesWithCapacityAndScoringViewModel { if (!this.viewModel) { throw new Error('Presenter has not been called yet'); } return this.viewModel; } private deriveDropPolicySummary(config: { championships: Array<{ dropScorePolicy: { strategy: string; count?: number; dropCount?: number }; }>; }): string { const championship = config.championships[0]; if (!championship) { return 'All results count'; } const policy = championship.dropScorePolicy; if (!policy || policy.strategy === 'none') { return 'All results count'; } if (policy.strategy === 'bestNResults' && typeof policy.count === 'number') { return `Best ${policy.count} results count`; } if ( policy.strategy === 'dropWorstN' && typeof policy.dropCount === 'number' ) { return `Worst ${policy.dropCount} results are dropped`; } return 'Custom drop score rules'; } }