/** * Domain Value Object: LeagueVisibility * * Represents the visibility and ranking status of a league. * This is a hardened version that enforces strict business rules. */ import type { IValueObject } from '@core/shared/domain'; import { RacingDomainValidationError } from '../errors/RacingDomainError'; export type LeagueVisibilityType = 'ranked' | 'unranked'; export interface LeagueVisibilityConstraints { readonly minDrivers: number; readonly maxDrivers: number; readonly isPubliclyVisible: boolean; readonly affectsRatings: boolean; readonly requiresApproval: boolean; } const VISIBILITY_CONSTRAINTS: Record = { ranked: { minDrivers: 10, maxDrivers: 100, isPubliclyVisible: true, affectsRatings: true, requiresApproval: false, }, unranked: { minDrivers: 2, maxDrivers: 50, isPubliclyVisible: false, affectsRatings: false, requiresApproval: true, }, }; export interface LeagueVisibilityProps { type: LeagueVisibilityType; } export class LeagueVisibility implements IValueObject { readonly type: LeagueVisibilityType; readonly constraints: LeagueVisibilityConstraints; private constructor(type: LeagueVisibilityType) { this.type = type; this.constraints = VISIBILITY_CONSTRAINTS[type]; } static ranked(): LeagueVisibility { return new LeagueVisibility('ranked'); } static unranked(): LeagueVisibility { return new LeagueVisibility('unranked'); } static fromString(value: string): LeagueVisibility { if (value === 'ranked' || value === 'public') { return LeagueVisibility.ranked(); } if (value === 'unranked' || value === 'private') { return LeagueVisibility.unranked(); } throw new RacingDomainValidationError(`Invalid league visibility: ${value}`); } /** * Validates that the given driver count meets the minimum requirement */ validateDriverCount(driverCount: number): { valid: boolean; error?: string } { if (driverCount < this.constraints.minDrivers) { return { valid: false, error: `${this.type === 'ranked' ? 'Ranked' : 'Unranked'} leagues require at least ${this.constraints.minDrivers} drivers` }; } if (driverCount > this.constraints.maxDrivers) { return { valid: false, error: `${this.type === 'ranked' ? 'Ranked' : 'Unranked'} leagues cannot exceed ${this.constraints.maxDrivers} drivers` }; } return { valid: true }; } /** * Validates that the given max participants is appropriate for this visibility */ validateMaxParticipants(maxParticipants: number): { valid: boolean; error?: string } { if (maxParticipants < this.constraints.minDrivers) { return { valid: false, error: `Max participants must be at least ${this.constraints.minDrivers} for ${this.type} leagues` }; } if (maxParticipants > this.constraints.maxDrivers) { return { valid: false, error: `Max participants cannot exceed ${this.constraints.maxDrivers} for ${this.type} leagues` }; } return { valid: true }; } /** * Check if this is a ranked/public league */ isRanked(): boolean { return this.type === 'ranked'; } /** * Check if this is an unranked/private league */ isUnranked(): boolean { return this.type === 'unranked'; } /** * Get minimum required drivers */ getMinDrivers(): number { return this.constraints.minDrivers; } /** * Get maximum allowed drivers */ getMaxDrivers(): number { return this.constraints.maxDrivers; } /** * Check if the given driver count meets minimum requirements */ meetsMinimumForVisibility(driverCount: number): boolean { return driverCount >= this.constraints.minDrivers; } /** * Convert to string for serialization */ toString(): LeagueVisibilityType { return this.type; } /** * For backward compatibility with existing 'public'/'private' terminology */ toLegacyString(): 'public' | 'private' { return this.type === 'ranked' ? 'public' : 'private'; } get props(): LeagueVisibilityProps { return { type: this.type }; } equals(other: IValueObject): boolean { return this.props.type === other.props.type; } } // Export constants for validation export const MIN_RANKED_LEAGUE_DRIVERS = VISIBILITY_CONSTRAINTS.ranked.minDrivers; export const MAX_RANKED_LEAGUE_DRIVERS = VISIBILITY_CONSTRAINTS.ranked.maxDrivers; export const MIN_UNRANKED_LEAGUE_DRIVERS = VISIBILITY_CONSTRAINTS.unranked.minDrivers; export const MAX_UNRANKED_LEAGUE_DRIVERS = VISIBILITY_CONSTRAINTS.unranked.maxDrivers;