import type { IRaceRepository } from '../../domain/repositories/IRaceRepository'; import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository'; import type { IResultRepository } from '../../domain/repositories/IResultRepository'; import type { IStandingRepository } from '../../domain/repositories/IStandingRepository'; import type { DriverRatingProvider } from '../ports/DriverRatingProvider'; import { Result } from '../../domain/entities/Result'; import { Standing } from '../../domain/entities/Standing'; import { RaceResultGenerator } from '../utils/RaceResultGenerator'; import { RatingUpdateService } from '@gridpilot/identity/domain/services/RatingUpdateService'; import type { AsyncUseCase } from '@gridpilot/shared/application'; import type { ILogger } from '../../../shared/src/logging/ILogger'; /** * Enhanced CompleteRaceUseCase that includes rating updates */ export interface CompleteRaceCommandDTO { raceId: string; } export class CompleteRaceUseCaseWithRatings implements AsyncUseCase { constructor( private readonly raceRepository: IRaceRepository, private readonly raceRegistrationRepository: IRaceRegistrationRepository, private readonly resultRepository: IResultRepository, private readonly standingRepository: IStandingRepository, private readonly driverRatingProvider: DriverRatingProvider, private readonly ratingUpdateService: RatingUpdateService, private readonly logger: ILogger, ) {} async execute(command: CompleteRaceCommandDTO): Promise { const { raceId } = command; this.logger.debug(`Attempting to complete race with ID: ${raceId}`); try { const race = await this.raceRepository.findById(raceId); if (!race) { this.logger.error(`Race not found for ID: ${raceId}`); throw new Error('Race not found'); } this.logger.debug(`Found race: ${race.id}`); // Get registered drivers for this race const registeredDriverIds = await this.raceRegistrationRepository.getRegisteredDrivers(raceId); if (registeredDriverIds.length === 0) { this.logger.warn(`No registered drivers for race ID: ${raceId}. Cannot complete race.`); throw new Error('Cannot complete race with no registered drivers'); } this.logger.debug(`Found ${registeredDriverIds.length} registered drivers for race ID: ${raceId}`); // Get driver ratings this.logger.debug('Fetching driver ratings...'); const driverRatings = this.driverRatingProvider.getRatings(registeredDriverIds); this.logger.debug('Driver ratings fetched.'); // Generate realistic race results this.logger.debug('Generating race results...'); const results = RaceResultGenerator.generateRaceResults(raceId, registeredDriverIds, driverRatings); this.logger.info(`Generated ${results.length} race results for race ID: ${raceId}`); // Save results this.logger.debug('Saving race results...'); for (const result of results) { await this.resultRepository.create(result); } this.logger.info('Race results saved successfully.'); // Update standings this.logger.debug(`Updating standings for league ID: ${race.leagueId}`); await this.updateStandings(race.leagueId, results); this.logger.info('Standings updated successfully.'); // Update driver ratings based on performance this.logger.debug('Updating driver ratings...'); await this.updateDriverRatings(results, registeredDriverIds.length); this.logger.info('Driver ratings updated successfully.'); // Complete the race this.logger.debug(`Marking race ID: ${raceId} as complete...`); const completedRace = race.complete(); await this.raceRepository.update(completedRace); this.logger.info(`Race ID: ${raceId} completed successfully.`); } catch (error: any) { this.logger.error(`Error completing race ${raceId}: ${error.message}`); throw error; } } private async updateStandings(leagueId: string, results: Result[]): Promise { // Group results by driver const resultsByDriver = new Map(); for (const result of results) { const existing = resultsByDriver.get(result.driverId) || []; existing.push(result); resultsByDriver.set(result.driverId, existing); } // Update or create standings for each driver for (const [driverId, driverResults] of resultsByDriver) { let standing = await this.standingRepository.findByDriverIdAndLeagueId(driverId, leagueId); if (!standing) { standing = Standing.create({ leagueId, driverId, }); } // Add all results for this driver (should be just one for this race) for (const result of driverResults) { standing = standing.addRaceResult(result.position, { 1: 25, 2: 18, 3: 15, 4: 12, 5: 10, 6: 8, 7: 6, 8: 4, 9: 2, 10: 1 }); } await this.standingRepository.save(standing); } } private async updateDriverRatings(results: Result[], totalDrivers: number): Promise { const driverResults = results.map(result => ({ driverId: result.driverId, position: result.position, totalDrivers, incidents: result.incidents, startPosition: result.startPosition, })); await this.ratingUpdateService.updateDriverRatingsAfterRace(driverResults); } }