import type { ChampionshipConfig } from '../types/ChampionshipConfig'; import type { ParticipantRef } from '../types/ParticipantRef'; import { ChampionshipStanding } from '../entities/championship/ChampionshipStanding'; import type { ParticipantEventPoints } from './EventScoringService'; import { DropScoreApplier, type EventPointsEntry } from './DropScoreApplier'; export class ChampionshipAggregator { constructor(private readonly dropScoreApplier: DropScoreApplier) {} aggregate(params: { seasonId: string; championship: ChampionshipConfig; eventPointsByEventId: Record; }): ChampionshipStanding[] { const { seasonId, championship, eventPointsByEventId } = params; const perParticipantEvents = new Map< string, { participant: ParticipantRef; events: EventPointsEntry[] } >(); for (const [eventId, pointsList] of Object.entries(eventPointsByEventId)) { for (const entry of pointsList) { const key = entry.participant.id; const existing = perParticipantEvents.get(key); const eventEntry: EventPointsEntry = { eventId, points: entry.totalPoints, }; if (existing) { existing.events.push(eventEntry); } else { perParticipantEvents.set(key, { participant: entry.participant, events: [eventEntry], }); } } } const standings: ChampionshipStanding[] = []; for (const { participant, events } of perParticipantEvents.values()) { const dropResult = this.dropScoreApplier.apply( championship.dropScorePolicy, events, ); const totalPoints = dropResult.totalPoints; const resultsCounted = dropResult.counted.length; const resultsDropped = dropResult.dropped.length; standings.push( ChampionshipStanding.create({ seasonId, championshipId: championship.id, participant, totalPoints, resultsCounted, resultsDropped, position: 0, }), ); } standings.sort((a, b) => b.totalPoints.toNumber() - a.totalPoints.toNumber()); return standings.map((s, index) => s.withPosition(index + 1)); } }