import type { Logger, UseCaseOutputPort } from '@core/shared/application'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import { Race } from '../../domain/entities/Race'; import type { Season } from '../../domain/entities/season/Season'; import type { IRaceRepository } from '../../domain/repositories/IRaceRepository'; import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository'; import { SeasonScheduleGenerator } from '../../domain/services/SeasonScheduleGenerator'; export type CreateLeagueSeasonScheduleRaceInput = { leagueId: string; seasonId: string; track: string; car: string; scheduledAt: Date; }; export type CreateLeagueSeasonScheduleRaceResult = { raceId: string; }; export type CreateLeagueSeasonScheduleRaceErrorCode = | 'SEASON_NOT_FOUND' | 'RACE_OUTSIDE_SEASON_WINDOW' | 'INVALID_INPUT' | 'REPOSITORY_ERROR'; export class CreateLeagueSeasonScheduleRaceUseCase { constructor( private readonly seasonRepository: ISeasonRepository, private readonly raceRepository: IRaceRepository, private readonly logger: Logger, private readonly output: UseCaseOutputPort, private readonly deps: { generateRaceId: () => string }, ) {} async execute( input: CreateLeagueSeasonScheduleRaceInput, ): Promise< Result< void, ApplicationErrorCode > > { this.logger.debug('Creating league season schedule race', { leagueId: input.leagueId, seasonId: input.seasonId, }); try { const season = await this.seasonRepository.findById(input.seasonId); if (!season || season.leagueId !== input.leagueId) { return Result.err({ code: 'SEASON_NOT_FOUND', details: { message: 'Season not found for league' }, }); } if (!this.isWithinSeasonWindow(season, input.scheduledAt)) { return Result.err({ code: 'RACE_OUTSIDE_SEASON_WINDOW', details: { message: 'Race scheduledAt is outside the season schedule window' }, }); } const id = this.deps.generateRaceId(); let race: Race; try { race = Race.create({ id, leagueId: input.leagueId, track: input.track, car: input.car, scheduledAt: input.scheduledAt, }); } catch (err) { const message = err instanceof Error ? err.message : 'Invalid race input'; return Result.err({ code: 'INVALID_INPUT', details: { message } }); } await this.raceRepository.create(race); const result: CreateLeagueSeasonScheduleRaceResult = { raceId: race.id }; this.output.present(result); return Result.ok(undefined); } catch (err) { const error = err instanceof Error ? err : new Error('Unknown error'); this.logger.error('Failed to create league season schedule race', error, { leagueId: input.leagueId, seasonId: input.seasonId, }); return Result.err({ code: 'REPOSITORY_ERROR', details: { message: error.message }, }); } } private isWithinSeasonWindow(season: Season, scheduledAt: Date): boolean { const { start, endInclusive } = this.getSeasonDateWindow(season); if (!start && !endInclusive) return true; const t = scheduledAt.getTime(); if (start && t < start.getTime()) return false; if (endInclusive && t > endInclusive.getTime()) return false; return true; } private getSeasonDateWindow(season: Season): { start?: Date; endInclusive?: Date } { const start = season.startDate ?? season.schedule?.startDate; const window: { start?: Date; endInclusive?: Date } = {}; if (start) { window.start = start; } if (season.endDate) { window.endInclusive = season.endDate; return window; } if (season.schedule) { const slots = SeasonScheduleGenerator.generateSlotsUpTo( season.schedule, season.schedule.plannedRounds, ); const last = slots.at(-1); if (last?.scheduledAt) { window.endInclusive = last.scheduledAt; } return window; } return window; } }