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 { IDriverRepository } from '../../domain/repositories/IDriverRepository'; import type { ITeamRepository } from '../../domain/repositories/ITeamRepository'; import type { LeagueDriverSeasonStatsOutputPort } from '../ports/output/LeagueDriverSeasonStatsOutputPort'; 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 }, LeagueDriverSeasonStatsOutputPort, 'NO_ERROR'> { constructor( private readonly standingRepository: IStandingRepository, private readonly resultRepository: IResultRepository, private readonly penaltyRepository: IPenaltyRepository, private readonly raceRepository: IRaceRepository, private readonly driverRepository: IDriverRepository, private readonly teamRepository: ITeamRepository, 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); } // Fetch drivers and teams const driverIds = standings.map(s => s.driverId); const drivers = await Promise.all(driverIds.map(id => this.driverRepository.findById(id))); const driversMap = new Map(drivers.filter(d => d).map(d => [d!.id, d!])); const teamIds = Array.from(new Set(drivers.filter(d => d?.teamId).map(d => d!.teamId!))); const teams = await Promise.all(teamIds.map(id => this.teamRepository.findById(id))); const teamsMap = new Map(teams.filter(t => t).map(t => [t!.id, t!])); // Compute stats const stats = standings.map(standing => { const driver = driversMap.get(standing.driverId); const team = driver?.teamId ? teamsMap.get(driver.teamId) : undefined; const penalties = penaltiesByDriver.get(standing.driverId) ?? { baseDelta: 0, bonusDelta: 0 }; const results = driverResults.get(standing.driverId) ?? []; const rating = driverRatings.get(standing.driverId); const racesStarted = results.length; const racesFinished = results.filter(r => r.position > 0).length; const dnfs = results.filter(r => r.position === 0).length; const noShows = races.length - racesStarted; const avgFinish = results.length > 0 ? results.reduce((sum, r) => sum + r.position, 0) / results.length : null; const pointsPerRace = racesStarted > 0 ? standing.points / racesStarted : 0; return { leagueId, driverId: standing.driverId, position: standing.position, driverName: driver?.name ?? '', teamId: driver?.teamId ?? undefined, teamName: team?.name ?? undefined, totalPoints: standing.points, basePoints: standing.points - penalties.baseDelta, penaltyPoints: penalties.baseDelta, bonusPoints: penalties.bonusDelta, pointsPerRace, racesStarted, racesFinished, dnfs, noShows, avgFinish, rating: rating?.rating ?? null, ratingChange: rating?.ratingChange ?? null, }; }); return Result.ok({ leagueId, stats, }); } }