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 { Result as RaceResult } from '../../domain/entities/result/Result'; import { Standing } from '../../domain/entities/Standing'; import { RaceResultGenerator } from '../utils/RaceResultGenerator'; import { RatingUpdateService } from '@core/identity/domain/services/RatingUpdateService'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort'; export interface CompleteRaceWithRatingsInput { raceId: string; } export type CompleteRaceWithRatingsResult = { raceId: string; ratingsUpdatedForDriverIds: string[]; }; export type CompleteRaceWithRatingsErrorCode = | 'RACE_NOT_FOUND' | 'NO_REGISTERED_DRIVERS' | 'ALREADY_COMPLETED' | 'RATING_UPDATE_FAILED' | 'REPOSITORY_ERROR'; interface DriverRatingProvider { getRatings(driverIds: string[]): Map; } /** * Enhanced CompleteRaceUseCase that includes rating updates. */ export class CompleteRaceUseCaseWithRatings { 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 output: UseCaseOutputPort, ) {} async execute(command: CompleteRaceWithRatingsInput): Promise< Result> > { try { const { raceId } = command; const race = await this.raceRepository.findById(raceId); if (!race) { return Result.err({ code: 'RACE_NOT_FOUND', details: { message: 'Race not found' } }); } if (race.status === 'completed') { return Result.err({ code: 'ALREADY_COMPLETED', details: { message: 'Race already completed' } }); } const registeredDriverIds = await this.raceRegistrationRepository.getRegisteredDrivers(raceId); if (registeredDriverIds.length === 0) { return Result.err({ code: 'NO_REGISTERED_DRIVERS', details: { message: 'No registered drivers' } }); } const driverRatings = this.driverRatingProvider.getRatings(registeredDriverIds); const results = RaceResultGenerator.generateRaceResults(raceId, registeredDriverIds, driverRatings); for (const result of results) { await this.resultRepository.create(result); } await this.updateStandings(race.leagueId, results); try { await this.updateDriverRatings(results, registeredDriverIds.length); } catch (error) { return Result.err({ code: 'RATING_UPDATE_FAILED', details: { message: error instanceof Error ? error.message : 'Failed to update driver ratings', }, }); } const completedRace = race.complete(); await this.raceRepository.update(completedRace); this.output.present({ raceId, ratingsUpdatedForDriverIds: registeredDriverIds, }); return Result.ok(undefined); } catch (error) { return Result.err({ code: 'REPOSITORY_ERROR', details: { message: error instanceof Error ? error.message : 'Unknown error', }, }); } } private async updateStandings(leagueId: string, results: RaceResult[]): Promise { const resultsByDriver = new Map(); for (const result of results) { const driverIdStr = result.driverId.toString(); const existing = resultsByDriver.get(driverIdStr) || []; existing.push(result); resultsByDriver.set(driverIdStr, existing); } for (const [driverIdStr, driverResults] of resultsByDriver) { let standing = await this.standingRepository.findByDriverIdAndLeagueId(driverIdStr, leagueId); if (!standing) { standing = Standing.create({ leagueId, driverId: driverIdStr, }); } for (const result of driverResults) { standing = standing.addRaceResult(result.position.toNumber(), { 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: RaceResult[], totalDrivers: number): Promise { const driverResults = results.map((result) => ({ driverId: result.driverId.toString(), position: result.position.toNumber(), totalDrivers, incidents: result.incidents.toNumber(), startPosition: result.startPosition.toNumber(), })); await this.ratingUpdateService.updateDriverRatingsAfterRace(driverResults); } }