import { League } from '@core/racing/domain/entities/League'; import { Race } from '@core/racing/domain/entities/Race'; import { Track } from '@core/racing/domain/entities/Track'; import { seedId } from './SeedIdHelper'; export class RacingRaceFactory { constructor( private readonly baseDate: Date, private readonly persistence: 'postgres' | 'inmemory' = 'inmemory', ) {} create(leagues: League[], tracks: Track[]): Race[] { const cars = ['GT3 – Porsche 911', 'GT3 – BMW M4', 'LMP3 Prototype', 'GT4 – Alpine', 'Touring – Civic']; const leagueIds = leagues.map((l) => l.id.toString()); const trackIds = tracks.map((t) => t.id); const demoLeagueId = 'league-5'; const races: Race[] = []; // Create races with systematic coverage of different statuses and scenarios const statuses: Array<'scheduled' | 'running' | 'completed' | 'cancelled'> = ['scheduled', 'running', 'completed', 'cancelled']; for (let i = 1; i <= 100; i++) { const leagueId = leagueIds[(i - 1) % leagueIds.length] ?? demoLeagueId; const trackId = trackIds[(i - 1) % trackIds.length]!; const track = tracks.find(t => t.id === trackId)!; // Determine status systematically to ensure coverage let status: 'scheduled' | 'running' | 'completed' | 'cancelled'; let scheduledAt: Date; if (i <= 4) { // First 4 races: one of each status status = statuses[i - 1]!; scheduledAt = this.addDays(this.baseDate, i <= 2 ? -35 + i : 1 + (i - 2) * 2); } else if (i <= 10) { // Next 6: completed races status = 'completed'; scheduledAt = this.addDays(this.baseDate, -35 + i); } else if (i <= 15) { // Next 5: scheduled future races status = 'scheduled'; scheduledAt = this.addDays(this.baseDate, 1 + (i - 10) * 3); } else if (i <= 20) { // Next 5: cancelled races status = 'cancelled'; scheduledAt = this.addDays(this.baseDate, -20 + (i - 15)); } else { // Rest: mix of scheduled and completed status = i % 3 === 0 ? 'completed' : 'scheduled'; scheduledAt = status === 'completed' ? this.addDays(this.baseDate, -10 + (i - 20)) : this.addDays(this.baseDate, 5 + (i - 20) * 2); } const base = { id: seedId(`race-${i}`, this.persistence), leagueId, scheduledAt, track: track.name.toString(), trackId: track.id, car: cars[(i - 1) % cars.length]!, }; // Special case for running race if (status === 'running') { races.push( Race.create({ ...base, status: 'running', strengthOfField: 45 + (i % 50), // Valid SOF: 0-100 registeredCount: 12 + (i % 5), // Varying registration counts maxParticipants: 24, // Ensure max is set }), ); continue; } // Add varying SOF and registration counts for completed races if (status === 'completed') { races.push( Race.create({ ...base, status: 'completed', strengthOfField: 35 + (i % 60), // Valid SOF: 0-100 registeredCount: 8 + (i % 8), maxParticipants: 20, // Ensure max is set }), ); continue; } // Scheduled races with some having registration data if (status === 'scheduled') { const hasRegistrations = i % 4 !== 0; // 75% have registrations races.push( Race.create({ ...base, status: 'scheduled', ...(hasRegistrations && { strengthOfField: 40 + (i % 55), // Valid SOF: 0-100 registeredCount: 5 + (i % 10), maxParticipants: 16 + (i % 10), // Ensure max is set and reasonable }), }), ); continue; } // Cancelled races races.push( Race.create({ ...base, status: 'cancelled', }), ); } return races; } private addDays(date: Date, days: number): Date { return new Date(date.getTime() + days * 24 * 60 * 60 * 1000); } }