import { ChampionshipAggregator } from '@core/racing/domain/services/ChampionshipAggregator'; import { EventScoringService } from '@core/racing/domain/services/EventScoringService'; import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig'; import type { SessionType } from '@core/racing/domain/types/SessionType'; import { ChampionshipStanding } from '../../domain/entities/championship/ChampionshipStanding'; import type { Logger } from '@core/shared/domain/Logger'; import { Result } from '@core/shared/domain/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import { ChampionshipStandingRepository } from '../../domain/repositories/ChampionshipStandingRepository'; import { LeagueRepository } from '../../domain/repositories/LeagueRepository'; import { LeagueScoringConfigRepository } from '../../domain/repositories/LeagueScoringConfigRepository'; import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository'; import { RaceRepository } from '../../domain/repositories/RaceRepository'; import { ResultRepository } from '../../domain/repositories/ResultRepository'; import { SeasonRepository } from '../../domain/repositories/SeasonRepository'; export type RecalculateChampionshipStandingsInput = { leagueId: string; seasonId: string; }; export type ChampionshipStandingsEntry = { driverId: string | null; teamId: string | null; position: number; points: number; }; export type RecalculateChampionshipStandingsResult = { leagueId: string; seasonId: string; entries: ChampionshipStandingsEntry[]; }; export type RecalculateChampionshipStandingsErrorCode = | 'LEAGUE_NOT_FOUND' | 'SEASON_NOT_FOUND' | 'REPOSITORY_ERROR'; export class RecalculateChampionshipStandingsUseCase { constructor(private readonly leagueRepository: LeagueRepository, private readonly seasonRepository: SeasonRepository, private readonly leagueScoringConfigRepository: LeagueScoringConfigRepository, private readonly raceRepository: RaceRepository, private readonly resultRepository: ResultRepository, private readonly penaltyRepository: PenaltyRepository, private readonly championshipStandingRepository: ChampionshipStandingRepository, private readonly eventScoringService: EventScoringService, private readonly championshipAggregator: ChampionshipAggregator, private readonly logger: Logger) {} async execute( input: RecalculateChampionshipStandingsInput, ): Promise< Result< RecalculateChampionshipStandingsResult, ApplicationErrorCode > > { const { leagueId, seasonId } = input; try { const league = await this.leagueRepository.findById(leagueId); if (!league) { return Result.err({ code: 'LEAGUE_NOT_FOUND', details: { message: `League not found: ${leagueId}` }, }); } const season = await this.seasonRepository.findById(seasonId); if (!season || season.leagueId !== leagueId) { return Result.err({ code: 'SEASON_NOT_FOUND', details: { message: `Season not found for league: leagueId=${leagueId}, seasonId=${seasonId}`, }, }); } const leagueScoringConfig = await this.leagueScoringConfigRepository.findBySeasonId(seasonId); if (!leagueScoringConfig) { throw new Error(`League scoring config not found for season: ${seasonId}`); } const championship = this.findChampionshipConfig(leagueScoringConfig.championships); const races = await this.raceRepository.findByLeagueId(leagueId); const eventPointsByEventId: Record> = {}; for (const race of races) { const sessionType = this.mapRaceSessionType(String(race.sessionType)); if (!championship.sessionTypes.includes(sessionType)) { continue; } const results = await this.resultRepository.findByRaceId(race.id); const penalties = await this.penaltyRepository.findByRaceId(race.id); const participantPoints = this.eventScoringService.scoreSession({ seasonId, championship, sessionType, results, penalties, }); eventPointsByEventId[race.id] = participantPoints; } const standings: ChampionshipStanding[] = this.championshipAggregator.aggregate({ seasonId, championship, eventPointsByEventId, }); await this.championshipStandingRepository.saveAll(standings); const result: RecalculateChampionshipStandingsResult = { leagueId, seasonId, entries: standings.map((standing) => ({ driverId: standing.participant?.id ?? null, teamId: null, position: standing.position.toNumber(), points: standing.totalPoints.toNumber(), })), }; return Result.ok(result); } catch (error) { const err = error as Error; this.logger.error('Failed to recalculate championship standings', err, { leagueId, seasonId, }); return Result.err({ code: 'REPOSITORY_ERROR', details: { message: err.message || 'Failed to recalculate championship standings', }, }); } } private findChampionshipConfig(championships: ChampionshipConfig[]): ChampionshipConfig { if (!championships || championships.length === 0) { throw new Error('No championship configurations found'); } return championships[0]!; } private mapRaceSessionType(sessionType: SessionType | string): SessionType { if (sessionType === 'race') { return 'main'; } if ( sessionType === 'practice' || sessionType === 'qualifying' || sessionType === 'timeTrial' ) { return sessionType; } return 'main'; } }