import type { IStandingRepository } from '../../domain/repositories/IStandingRepository'; import type { IResultRepository } from '../../domain/repositories/IResultRepository'; import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository'; import type { IRaceRepository } from '../../domain/repositories/IRaceRepository'; import type { ILeagueDriverSeasonStatsPresenter } from '../presenters/ILeagueDriverSeasonStatsPresenter'; export interface DriverRatingPort { getRating(driverId: string): { rating: number | null; ratingChange: number | null }; } export interface GetLeagueDriverSeasonStatsUseCaseParams { leagueId: string; } /** * Use Case for retrieving league driver season statistics. * Orchestrates domain logic and delegates presentation to the presenter. */ export class GetLeagueDriverSeasonStatsUseCase { constructor( private readonly standingRepository: IStandingRepository, private readonly resultRepository: IResultRepository, private readonly penaltyRepository: IPenaltyRepository, private readonly raceRepository: IRaceRepository, private readonly driverRatingPort: DriverRatingPort, public readonly presenter: ILeagueDriverSeasonStatsPresenter, ) {} async execute(params: GetLeagueDriverSeasonStatsUseCaseParams): Promise { const { leagueId } = params; // Get standings and races for the league const [standings, races] = await Promise.all([ this.standingRepository.findByLeagueId(leagueId), this.raceRepository.findByLeagueId(leagueId), ]); // Fetch all penalties for all races in the league const penaltiesArrays = await Promise.all( races.map(race => this.penaltyRepository.findByRaceId(race.id)) ); const penaltiesForLeague = penaltiesArrays.flat(); // Group penalties by driver for quick lookup const penaltiesByDriver = new Map(); for (const p of penaltiesForLeague) { // Only count applied penalties if (p.status !== 'applied') continue; const current = penaltiesByDriver.get(p.driverId) ?? { baseDelta: 0, bonusDelta: 0 }; // Convert penalty to points delta based on type if (p.type === 'points_deduction' && p.value) { // Points deductions are negative current.baseDelta -= p.value; } penaltiesByDriver.set(p.driverId, current); } // Collect driver ratings const driverRatings = new Map(); for (const standing of standings) { const ratingInfo = this.driverRatingPort.getRating(standing.driverId); driverRatings.set(standing.driverId, ratingInfo); } // Collect driver results const driverResults = new Map>(); for (const standing of standings) { const results = await this.resultRepository.findByDriverIdAndLeagueId(standing.driverId, leagueId); driverResults.set(standing.driverId, results); } this.presenter.present( leagueId, standings, penaltiesByDriver, driverResults, driverRatings ); } }