Files
gridpilot.gg/core/racing/application/dto/LeagueScheduleDTO.ts
2025-12-15 13:46:07 +01:00

121 lines
4.4 KiB
TypeScript

import type { LeagueTimingsFormDTO } from './LeagueConfigFormDTO';
import type { Weekday } from '../../domain/types/Weekday';
import { RaceTimeOfDay } from '../../domain/value-objects/RaceTimeOfDay';
import { LeagueTimezone } from '../../domain/value-objects/LeagueTimezone';
import { WeekdaySet } from '../../domain/value-objects/WeekdaySet';
import { MonthlyRecurrencePattern } from '../../domain/value-objects/MonthlyRecurrencePattern';
import type { RecurrenceStrategy } from '../../domain/value-objects/RecurrenceStrategy';
import { RecurrenceStrategyFactory } from '../../domain/value-objects/RecurrenceStrategy';
import { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
import { BusinessRuleViolationError } from '../errors/RacingApplicationError';
export interface LeagueScheduleDTO {
seasonStartDate: string;
raceStartTime: string;
timezoneId: string;
recurrenceStrategy: 'weekly' | 'everyNWeeks' | 'monthlyNthWeekday';
intervalWeeks?: number | undefined;
weekdays?: Weekday[] | undefined;
monthlyOrdinal?: 1 | 2 | 3 | 4 | undefined;
monthlyWeekday?: Weekday | undefined;
plannedRounds: number;
}
export interface LeagueSchedulePreviewDTO {
rounds: Array<{ roundNumber: number; scheduledAt: string; timezoneId: string }>;
summary: string;
}
export function leagueTimingsToScheduleDTO(
timings: LeagueTimingsFormDTO,
): LeagueScheduleDTO | null {
if (
!timings.seasonStartDate ||
!timings.raceStartTime ||
!timings.timezoneId ||
!timings.recurrenceStrategy ||
!timings.roundsPlanned
) {
return null;
}
return {
seasonStartDate: timings.seasonStartDate,
raceStartTime: timings.raceStartTime,
timezoneId: timings.timezoneId,
recurrenceStrategy: timings.recurrenceStrategy,
intervalWeeks: timings.intervalWeeks,
weekdays: timings.weekdays,
monthlyOrdinal: timings.monthlyOrdinal,
monthlyWeekday: timings.monthlyWeekday,
plannedRounds: timings.roundsPlanned,
};
}
export function scheduleDTOToSeasonSchedule(dto: LeagueScheduleDTO): SeasonSchedule {
if (!dto.seasonStartDate) {
throw new BusinessRuleViolationError('seasonStartDate is required');
}
if (!dto.raceStartTime) {
throw new BusinessRuleViolationError('raceStartTime is required');
}
if (!dto.timezoneId) {
throw new BusinessRuleViolationError('timezoneId is required');
}
if (!dto.recurrenceStrategy) {
throw new BusinessRuleViolationError('recurrenceStrategy is required');
}
if (!Number.isInteger(dto.plannedRounds) || dto.plannedRounds <= 0) {
throw new BusinessRuleViolationError('plannedRounds must be a positive integer');
}
const startDate = new Date(dto.seasonStartDate);
if (Number.isNaN(startDate.getTime())) {
throw new BusinessRuleViolationError(
`seasonStartDate must be a valid date, got "${dto.seasonStartDate}"`,
);
}
const timeOfDay = RaceTimeOfDay.fromString(dto.raceStartTime);
const timezone = new LeagueTimezone(dto.timezoneId);
let recurrence: RecurrenceStrategy;
if (dto.recurrenceStrategy === 'weekly') {
if (!dto.weekdays || dto.weekdays.length === 0) {
throw new BusinessRuleViolationError('weekdays are required for weekly recurrence');
}
recurrence = RecurrenceStrategyFactory.weekly(new WeekdaySet(dto.weekdays));
} else if (dto.recurrenceStrategy === 'everyNWeeks') {
if (!dto.weekdays || dto.weekdays.length === 0) {
throw new BusinessRuleViolationError('weekdays are required for everyNWeeks recurrence');
}
if (dto.intervalWeeks == null) {
throw new BusinessRuleViolationError(
'intervalWeeks is required for everyNWeeks recurrence',
);
}
recurrence = RecurrenceStrategyFactory.everyNWeeks(
dto.intervalWeeks,
new WeekdaySet(dto.weekdays),
);
} else if (dto.recurrenceStrategy === 'monthlyNthWeekday') {
if (!dto.monthlyOrdinal || !dto.monthlyWeekday) {
throw new BusinessRuleViolationError(
'monthlyOrdinal and monthlyWeekday are required for monthlyNthWeekday',
);
}
const pattern = new MonthlyRecurrencePattern(dto.monthlyOrdinal, dto.monthlyWeekday);
recurrence = RecurrenceStrategyFactory.monthlyNthWeekday(pattern);
} else {
throw new BusinessRuleViolationError(`Unknown recurrenceStrategy "${dto.recurrenceStrategy}"`);
}
return new SeasonSchedule({
startDate,
timeOfDay,
timezone,
recurrence,
plannedRounds: dto.plannedRounds,
});
}