import type { ValueObject } from '@core/shared/domain/ValueObject'; import type { StewardingDecisionMode } from '../entities/League'; import { RacingDomainValidationError } from '../errors/RacingDomainError'; export interface SeasonStewardingConfigProps { decisionMode: StewardingDecisionMode; requiredVotes?: number | undefined; requireDefense: boolean; defenseTimeLimit: number; voteTimeLimit: number; protestDeadlineHours: number; stewardingClosesHours: number; notifyAccusedOnProtest: boolean; notifyOnVoteRequired: boolean; } /** * Value Object: SeasonStewardingConfig * * Encapsulates stewarding configuration owned by a Season. * Shape intentionally mirrors LeagueStewardingFormDTO used by the wizard. */ export class SeasonStewardingConfig implements ValueObject { readonly decisionMode: StewardingDecisionMode; readonly requiredVotes?: number; readonly requireDefense: boolean; readonly defenseTimeLimit: number; readonly voteTimeLimit: number; readonly protestDeadlineHours: number; readonly stewardingClosesHours: number; readonly notifyAccusedOnProtest: boolean; readonly notifyOnVoteRequired: boolean; constructor(props: SeasonStewardingConfigProps) { if (!props.decisionMode) { throw new RacingDomainValidationError( 'SeasonStewardingConfig.decisionMode is required', ); } if ( (props.decisionMode === 'steward_vote' || props.decisionMode === 'member_vote' || props.decisionMode === 'steward_veto' || props.decisionMode === 'member_veto') && (props.requiredVotes === undefined || !Number.isInteger(props.requiredVotes) || props.requiredVotes <= 0) ) { throw new RacingDomainValidationError( 'SeasonStewardingConfig.requiredVotes must be a positive integer for voting/veto modes', ); } // For non-voting modes, requiredVotes should not be provided if (props.decisionMode !== 'steward_vote' && props.decisionMode !== 'member_vote' && props.decisionMode !== 'steward_veto' && props.decisionMode !== 'member_veto' && props.requiredVotes !== undefined) { throw new RacingDomainValidationError( 'SeasonStewardingConfig.requiredVotes should only be provided for voting/veto modes', ); } if (!Number.isInteger(props.defenseTimeLimit) || props.defenseTimeLimit < 0) { throw new RacingDomainValidationError( 'SeasonStewardingConfig.defenseTimeLimit must be a non-negative integer (hours)', ); } if (!Number.isInteger(props.voteTimeLimit) || props.voteTimeLimit <= 0) { throw new RacingDomainValidationError( 'SeasonStewardingConfig.voteTimeLimit must be a positive integer (hours)', ); } if ( !Number.isInteger(props.protestDeadlineHours) || props.protestDeadlineHours <= 0 ) { throw new RacingDomainValidationError( 'SeasonStewardingConfig.protestDeadlineHours must be a positive integer (hours)', ); } if ( !Number.isInteger(props.stewardingClosesHours) || props.stewardingClosesHours <= 0 ) { throw new RacingDomainValidationError( 'SeasonStewardingConfig.stewardingClosesHours must be a positive integer (hours)', ); } this.decisionMode = props.decisionMode; if (props.requiredVotes !== undefined) { this.requiredVotes = props.requiredVotes; } this.requireDefense = props.requireDefense; this.defenseTimeLimit = props.defenseTimeLimit; this.voteTimeLimit = props.voteTimeLimit; this.protestDeadlineHours = props.protestDeadlineHours; this.stewardingClosesHours = props.stewardingClosesHours; this.notifyAccusedOnProtest = props.notifyAccusedOnProtest; this.notifyOnVoteRequired = props.notifyOnVoteRequired; } get props(): SeasonStewardingConfigProps { return { decisionMode: this.decisionMode, ...(this.requiredVotes !== undefined ? { requiredVotes: this.requiredVotes } : {}), requireDefense: this.requireDefense, defenseTimeLimit: this.defenseTimeLimit, voteTimeLimit: this.voteTimeLimit, protestDeadlineHours: this.protestDeadlineHours, stewardingClosesHours: this.stewardingClosesHours, notifyAccusedOnProtest: this.notifyAccusedOnProtest, notifyOnVoteRequired: this.notifyOnVoteRequired, }; } equals(other: ValueObject): boolean { const a = this.props; const b = other.props; return ( a.decisionMode === b.decisionMode && a.requiredVotes === b.requiredVotes && a.requireDefense === b.requireDefense && a.defenseTimeLimit === b.defenseTimeLimit && a.voteTimeLimit === b.voteTimeLimit && a.protestDeadlineHours === b.protestDeadlineHours && a.stewardingClosesHours === b.stewardingClosesHours && a.notifyAccusedOnProtest === b.notifyAccusedOnProtest && a.notifyOnVoteRequired === b.notifyOnVoteRequired ); } }