import { v4 as uuidv4 } from 'uuid'; import { League } from '../../domain/entities/League'; import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository'; import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository'; import type { ILeagueScoringConfigRepository } from '../../domain/repositories/ILeagueScoringConfigRepository'; import type { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig'; import type { LeagueScoringPresetProvider, LeagueScoringPresetDTO, } from '../ports/LeagueScoringPresetProvider'; export interface CreateLeagueWithSeasonAndScoringCommand { name: string; description?: string; visibility: 'public' | 'private'; ownerId: string; gameId: string; maxDrivers?: number; maxTeams?: number; enableDriverChampionship: boolean; enableTeamChampionship: boolean; enableNationsChampionship: boolean; enableTrophyChampionship: boolean; scoringPresetId?: string; } export interface CreateLeagueWithSeasonAndScoringResultDTO { leagueId: string; seasonId: string; scoringPresetId?: string; scoringPresetName?: string; } export class CreateLeagueWithSeasonAndScoringUseCase { constructor( private readonly leagueRepository: ILeagueRepository, private readonly seasonRepository: ISeasonRepository, private readonly leagueScoringConfigRepository: ILeagueScoringConfigRepository, private readonly presetProvider: LeagueScoringPresetProvider, ) {} async execute( command: CreateLeagueWithSeasonAndScoringCommand, ): Promise { this.validate(command); const leagueId = uuidv4(); const league = League.create({ id: leagueId, name: command.name, description: command.description ?? '', ownerId: command.ownerId, settings: { pointsSystem: (command.scoringPresetId as any) ?? 'custom', maxDrivers: command.maxDrivers, }, }); await this.leagueRepository.create(league); const seasonId = uuidv4(); const season = { id: seasonId, leagueId: league.id, gameId: command.gameId, name: `${command.name} Season 1`, year: new Date().getFullYear(), order: 1, status: 'active' as const, startDate: new Date(), endDate: new Date(), }; // Season is a domain entity; use the repository's create, but shape matches Season.create expectations. // To keep this use case independent, we rely on repository to persist the plain object. // eslint-disable-next-line @typescript-eslint/no-explicit-any await this.seasonRepository.create(season as any); const presetId = command.scoringPresetId ?? 'club-default'; const preset: LeagueScoringPresetDTO | undefined = this.presetProvider.getPresetById(presetId); if (!preset) { throw new Error(`Unknown scoring preset: ${presetId}`); } const scoringConfig: LeagueScoringConfig = { id: uuidv4(), seasonId, scoringPresetId: preset.id, championships: [], }; // For the initial alpha slice, we keep using the preset's config shape from the in-memory registry. // The preset registry is responsible for building the full LeagueScoringConfig; we only attach the preset id here. const fullConfigFactory = (await import( '../../infrastructure/repositories/InMemoryScoringRepositories' )) as typeof import('../../infrastructure/repositories/InMemoryScoringRepositories'); const presetFromInfra = fullConfigFactory.getLeagueScoringPresetById( preset.id, ); if (!presetFromInfra) { throw new Error(`Preset registry missing preset: ${preset.id}`); } const infraConfig = presetFromInfra.createConfig({ seasonId }); const finalConfig: LeagueScoringConfig = { ...infraConfig, scoringPresetId: preset.id, }; await this.leagueScoringConfigRepository.save(finalConfig); return { leagueId: league.id, seasonId, scoringPresetId: preset.id, scoringPresetName: preset.name, }; } private validate(command: CreateLeagueWithSeasonAndScoringCommand): void { if (!command.name || command.name.trim().length === 0) { throw new Error('League name is required'); } if (!command.ownerId || command.ownerId.trim().length === 0) { throw new Error('League ownerId is required'); } if (!command.gameId || command.gameId.trim().length === 0) { throw new Error('gameId is required'); } if (!command.visibility) { throw new Error('visibility is required'); } if (command.maxDrivers !== undefined && command.maxDrivers <= 0) { throw new Error('maxDrivers must be greater than 0 when provided'); } } }