141 lines
4.7 KiB
TypeScript
141 lines
4.7 KiB
TypeScript
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<CreateLeagueWithSeasonAndScoringResultDTO> {
|
|
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');
|
|
}
|
|
}
|
|
} |