import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { IDriverRepository } from '../../domain/repositories/IDriverRepository'; import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository'; import type { IRaceRepository } from '../../domain/repositories/IRaceRepository'; import type { IResultRepository } from '../../domain/repositories/IResultRepository'; import type { IStandingRepository } from '../../domain/repositories/IStandingRepository'; import type { DriverRatingPort } from '../ports/DriverRatingPort'; export type DriverSeasonStats = { leagueId: string; driverId: string; position: number; driverName: string; teamId: string | undefined; teamName: string | undefined; totalPoints: number; basePoints: number; penaltyPoints: number; bonusPoints: number; pointsPerRace: number; racesStarted: number; racesFinished: number; dnfs: number; noShows: number; avgFinish: number | null; rating: number | null; ratingChange: number | null; }; export type GetLeagueDriverSeasonStatsInput = { leagueId: string; }; export type GetLeagueDriverSeasonStatsResult = { leagueId: string; stats: DriverSeasonStats[]; }; export type GetLeagueDriverSeasonStatsErrorCode = | 'LEAGUE_NOT_FOUND' | 'SEASON_NOT_FOUND' | 'DRIVER_NOT_FOUND' | 'REPOSITORY_ERROR'; /** * Use Case for retrieving league driver season statistics. * Orchestrates domain logic and returns the result. */ export class GetLeagueDriverSeasonStatsUseCase { constructor(private readonly standingRepository: IStandingRepository, private readonly resultRepository: IResultRepository, private readonly penaltyRepository: IPenaltyRepository, private readonly raceRepository: IRaceRepository, private readonly driverRepository: IDriverRepository, private readonly driverRatingPort: DriverRatingPort) {} async execute( input: GetLeagueDriverSeasonStatsInput, ): Promise< Result> > { try { const { leagueId } = input; const [standings, races] = await Promise.all([ this.standingRepository.findByLeagueId(leagueId), this.raceRepository.findByLeagueId(leagueId), ]); if (!standings || standings.length === 0) { return Result.err({ code: 'LEAGUE_NOT_FOUND', details: { message: 'League not found' }, }); } const penaltiesArrays = await Promise.all( races.map(race => this.penaltyRepository.findByRaceId(race.id)), ); const penaltiesForLeague = penaltiesArrays.flat(); const penaltiesByDriver = new Map(); for (const p of penaltiesForLeague) { if (p.status !== 'applied') continue; const current = penaltiesByDriver.get(p.driverId) ?? { baseDelta: 0, bonusDelta: 0 }; if (p.type === 'points_deduction' && p.value) { current.baseDelta -= p.value; } penaltiesByDriver.set(p.driverId, current); } const driverRatings = new Map(); for (const standing of standings) { const driverId = standing.driverId.toString(); const rating = await this.driverRatingPort.getDriverRating(driverId); driverRatings.set(driverId, { rating, ratingChange: null }); } const driverResults = new Map>(); for (const standing of standings) { const driverId = standing.driverId.toString(); const results = await this.resultRepository.findByDriverIdAndLeagueId(driverId, leagueId); driverResults.set( driverId, results.map(result => ({ position: result.position.toNumber() })), ); } const driverIds = standings.map(s => s.driverId.toString()); const drivers = await Promise.all(driverIds.map(id => this.driverRepository.findById(id))); const driversMap = new Map( drivers .filter((driver): driver is NonNullable => driver !== null) .map(driver => [driver.id, driver]), ); const stats: DriverSeasonStats[] = standings.map(standing => { const driverId = standing.driverId.toString(); const driver = driversMap.get(driverId); const penalties = penaltiesByDriver.get(driverId) ?? { baseDelta: 0, bonusDelta: 0 }; const results = driverResults.get(driverId) ?? []; const rating = driverRatings.get(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 totalPoints = standing.points.toNumber(); const pointsPerRace = racesStarted > 0 ? totalPoints / racesStarted : 0; return { leagueId, driverId, position: standing.position.toNumber(), driverName: driver ? driver.name.toString() : '', teamId: undefined, teamName: undefined, totalPoints, basePoints: totalPoints - penalties.baseDelta, penaltyPoints: penalties.baseDelta, bonusPoints: penalties.bonusDelta, pointsPerRace, racesStarted, racesFinished, dnfs, noShows, avgFinish, rating: rating?.rating ?? null, ratingChange: rating?.ratingChange ?? null, }; }); const result: GetLeagueDriverSeasonStatsResult = { leagueId, stats, }; return Result.ok(result); } catch (error) { const message = error instanceof Error && error.message ? error.message : 'Failed to fetch league driver season stats'; return Result.err({ code: 'REPOSITORY_ERROR', details: { message }, }); } } }