import { RacesApiClient } from '../../api/races/RacesApiClient'; import { RaceDetailEntryViewModel } from '../../view-models/RaceDetailEntryViewModel'; import { RaceDetailUserResultViewModel } from '../../view-models/RaceDetailUserResultViewModel'; import { RaceDetailViewModel } from '../../view-models/RaceDetailViewModel'; import { RacesPageViewModel } from '../../view-models/RacesPageViewModel'; import { RaceStatsViewModel } from '../../view-models/RaceStatsViewModel'; import type { RaceDetailsViewModel } from '../../view-models/RaceDetailsViewModel'; import type { FileProtestCommandDTO } from '../../types/generated/FileProtestCommandDTO'; import type { RaceStatsDTO } from '../../types/generated/RaceStatsDTO'; /** * Race Service * * Orchestrates race operations by coordinating API calls and view model creation. * All dependencies are injected via constructor. */ export class RaceService { constructor( private readonly apiClient: RacesApiClient ) {} /** * Get race detail with view model transformation */ async getRaceDetail( raceId: string, driverId: string ): Promise { const dto = await this.apiClient.getDetail(raceId, driverId); return new RaceDetailViewModel(dto, driverId); } /** * Get race details for pages/components (DTO-free shape) */ async getRaceDetails( raceId: string, driverId: string ): Promise { const dto: any = await this.apiClient.getDetail(raceId, driverId); const raceDto: any = dto?.race ?? null; const leagueDto: any = dto?.league ?? null; const registrationDto: any = dto?.registration ?? {}; const isUserRegistered = Boolean(registrationDto.isUserRegistered ?? registrationDto.isRegistered ?? false); const canRegister = Boolean(registrationDto.canRegister); const status = String(raceDto?.status ?? ''); const canReopenRace = status === 'completed' || status === 'cancelled'; return { race: raceDto ? { id: String(raceDto.id ?? ''), track: String(raceDto.track ?? ''), car: String(raceDto.car ?? ''), scheduledAt: String(raceDto.scheduledAt ?? ''), status, sessionType: String(raceDto.sessionType ?? ''), } : null, league: leagueDto ? { id: String(leagueDto.id ?? ''), name: String(leagueDto.name ?? ''), description: leagueDto.description ?? null, settings: leagueDto.settings, } : null, entryList: (dto?.entryList ?? []).map((entry: any) => new RaceDetailEntryViewModel(entry, driverId)), registration: { canRegister, isUserRegistered, }, userResult: dto?.userResult ? new RaceDetailUserResultViewModel(dto.userResult) : null, canReopenRace, error: dto?.error, }; } /** * Get races page data with view model transformation */ async getRacesPageData(): Promise { const dto = await this.apiClient.getPageData(); return new RacesPageViewModel(dto); } /** * Get races page data filtered by league */ async getLeagueRacesPageData(leagueId: string): Promise { const dto = await this.apiClient.getPageData(leagueId); return new RacesPageViewModel(dto); } /** * Get all races page data with view model transformation * Currently same as getRacesPageData, but can be extended for different filtering */ async getAllRacesPageData(): Promise { const dto = await this.apiClient.getPageData(); return new RacesPageViewModel(dto); } /** * Get total races statistics with view model transformation */ async getRacesTotal(): Promise { const dto: RaceStatsDTO = await this.apiClient.getTotal(); return new RaceStatsViewModel(dto); } /** * Register for a race */ async registerForRace(raceId: string, leagueId: string, driverId: string): Promise { await this.apiClient.register(raceId, { raceId, leagueId, driverId }); } /** * Withdraw from a race */ async withdrawFromRace(raceId: string, driverId: string): Promise { await this.apiClient.withdraw(raceId, { raceId, driverId }); } /** * Cancel a race */ async cancelRace(raceId: string): Promise { await this.apiClient.cancel(raceId); } /** * Complete a race */ async completeRace(raceId: string): Promise { await this.apiClient.complete(raceId); } /** * Re-open a race */ async reopenRace(raceId: string): Promise { await this.apiClient.reopen(raceId); } /** * File a protest */ async fileProtest(input: FileProtestCommandDTO): Promise { await this.apiClient.fileProtest(input); } /** * Find races by league ID * * The races API does not currently expose a league-filtered listing endpoint in this build, * so this method deliberately signals that the operation is unavailable instead of making * assumptions about URL structure. */ async findByLeagueId(leagueId: string): Promise { const page = await this.getLeagueRacesPageData(leagueId); return page.races; } }