import type { Race } from '@core/racing/domain/entities/Race'; import type { IRaceRepository } from '@core/racing/application/ports/IRaceRepository'; import type { IIsDriverRegisteredForRaceQuery } from '@core/racing/application/queries/IIsDriverRegisteredForRaceQuery'; import type { IRegisterForRaceUseCase } from '@core/racing/application/use-cases/IRegisterForRaceUseCase'; import type { IWithdrawFromRaceUseCase } from '@core/racing/application/use-cases/IWithdrawFromRaceUseCase'; export interface LeagueScheduleRaceItemViewModel { id: string; leagueId: string; track: string; car: string; sessionType: string; scheduledAt: Date; status: Race['status']; isUpcoming: boolean; isPast: boolean; isRegistered: boolean; } export interface LeagueScheduleViewModel { races: LeagueScheduleRaceItemViewModel[]; } export interface ILeagueSchedulePresenter { loadLeagueSchedule(leagueId: string, driverId: string): Promise; registerForRace(raceId: string, leagueId: string, driverId: string): Promise; withdrawFromRace(raceId: string, driverId: string): Promise; } export class LeagueSchedulePresenter implements ILeagueSchedulePresenter { constructor( private raceRepository: IRaceRepository, private isDriverRegisteredForRaceQuery: IIsDriverRegisteredForRaceQuery, private registerForRaceUseCase: IRegisterForRaceUseCase, private withdrawFromRaceUseCase: IWithdrawFromRaceUseCase, ) {} /** * Load league schedule with registration status for a given driver. */ async loadLeagueSchedule( leagueId: string, driverId: string, ): Promise { const allRaces = await this.raceRepository.findAll(); const leagueRaces = allRaces .filter((race) => race.leagueId === leagueId) .sort( (a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime(), ); const now = new Date(); const registrationStates: Record = {}; await Promise.all( leagueRaces.map(async (race) => { const registered = await this.isDriverRegisteredForRaceQuery.execute({ raceId: race.id, driverId, }); registrationStates[race.id] = registered; }), ); const races: LeagueScheduleRaceItemViewModel[] = leagueRaces.map((race) => { const raceDate = new Date(race.scheduledAt); const isPast = race.status === 'completed' || raceDate <= now; const isUpcoming = race.status === 'scheduled' && raceDate > now; return { id: race.id, leagueId: race.leagueId, track: race.track, car: race.car, sessionType: race.sessionType, scheduledAt: raceDate, status: race.status, isUpcoming, isPast, isRegistered: registrationStates[race.id] ?? false, }; }); return { races }; } /** * Register the driver for a race. */ async registerForRace( raceId: string, leagueId: string, driverId: string, ): Promise { await this.registerForRaceUseCase.execute({ raceId, leagueId, driverId, }); } /** * Withdraw the driver from a race. */ async withdrawFromRace( raceId: string, driverId: string, ): Promise { await this.withdrawFromRaceUseCase.execute({ raceId, driverId, }); } }