/** * Use Case: GetRaceWithSOFUseCase * * Returns race details enriched with calculated Strength of Field (SOF). * SOF is calculated from participant ratings if not already stored on the race. * Orchestrates domain logic and delegates presentation to the presenter. */ import type { IRaceRepository } from '../../domain/repositories/IRaceRepository'; import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository'; import type { IResultRepository } from '../../domain/repositories/IResultRepository'; import type { DriverRatingProvider } from '../ports/DriverRatingProvider'; import { AverageStrengthOfFieldCalculator, type StrengthOfFieldCalculator, } from '../../domain/services/StrengthOfFieldCalculator'; import type { IRaceWithSOFPresenter } from '../presenters/IRaceWithSOFPresenter'; export interface GetRaceWithSOFQueryParams { raceId: string; } export class GetRaceWithSOFUseCase { private readonly sofCalculator: StrengthOfFieldCalculator; constructor( private readonly raceRepository: IRaceRepository, private readonly registrationRepository: IRaceRegistrationRepository, private readonly resultRepository: IResultRepository, private readonly driverRatingProvider: DriverRatingProvider, public readonly presenter: IRaceWithSOFPresenter, sofCalculator?: StrengthOfFieldCalculator, ) { this.sofCalculator = sofCalculator ?? new AverageStrengthOfFieldCalculator(); } async execute(params: GetRaceWithSOFQueryParams): Promise { const { raceId } = params; const race = await this.raceRepository.findById(raceId); if (!race) { return null; } // Get participant IDs based on race status let participantIds: string[] = []; if (race.status === 'completed') { // For completed races, use results const results = await this.resultRepository.findByRaceId(raceId); participantIds = results.map(r => r.driverId); } else { // For upcoming/running races, use registrations participantIds = await this.registrationRepository.getRegisteredDrivers(raceId); } // Use stored SOF if available, otherwise calculate let strengthOfField = race.strengthOfField ?? null; if (strengthOfField === null && participantIds.length > 0) { const ratings = this.driverRatingProvider.getRatings(participantIds); const driverRatings = participantIds .filter(id => ratings.has(id)) .map(id => ({ driverId: id, rating: ratings.get(id)! })); strengthOfField = this.sofCalculator.calculate(driverRatings); } this.presenter.present( race.id, race.leagueId, race.scheduledAt, race.track, race.trackId, race.car, race.carId, race.sessionType, race.status, strengthOfField, race.registeredCount ?? participantIds.length, race.maxParticipants, participantIds.length ); } }