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 { LeagueDriverSeasonStatsResultDTO } from '../presenters/ILeagueDriverSeasonStatsPresenter'; import type { AsyncUseCase } from '@core/shared/application'; import { Result } from '@core/shared/application/Result'; import type { DriverRatingPort } from '../ports/DriverRatingPort'; /** * Use Case for retrieving league driver season statistics. * Orchestrates domain logic and returns the result. */ export class GetLeagueDriverSeasonStatsUseCase implements AsyncUseCase<{ leagueId: string }, LeagueDriverSeasonStatsResultDTO, 'NO_ERROR'> { constructor( private readonly standingRepository: IStandingRepository, private readonly resultRepository: IResultRepository, private readonly penaltyRepository: IPenaltyRepository, private readonly raceRepository: IRaceRepository, private readonly driverRatingPort: DriverRatingPort, ) {} async execute(params: { leagueId: string }): 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); } const dto: LeagueDriverSeasonStatsResultDTO = { leagueId, standings: standings.map(standing => ({ driverId: standing.driverId, position: standing.position, points: standing.points, racesCompleted: standing.racesCompleted, })), penalties: penaltiesByDriver, driverResults, driverRatings, }; return Result.ok(dto); } }