import type { IValueObject } from '@core/shared/domain'; /** * Incident types that can occur during a race */ export type IncidentType = | 'track_limits' // Driver went off track and gained advantage | 'contact' // Physical contact with another car | 'unsafe_rejoin' // Unsafe rejoining of the track | 'aggressive_driving' // Aggressive defensive or overtaking maneuvers | 'false_start' // Started before green flag | 'collision' // Major collision involving multiple cars | 'spin' // Driver spun out | 'mechanical' // Mechanical failure (not driver error) | 'other'; // Other incident types /** * Individual incident record */ export interface IncidentRecord { type: IncidentType; lap: number; description?: string; penaltyPoints?: number; // Points deducted for this incident } /** * Value Object: RaceIncidents * * Encapsulates all incidents that occurred during a driver's race. * Provides methods to calculate total penalty points and incident severity. */ export class RaceIncidents implements IValueObject { private readonly incidents: IncidentRecord[]; constructor(incidents: IncidentRecord[] = []) { this.incidents = [...incidents]; } get props(): IncidentRecord[] { return [...this.incidents]; } /** * Add a new incident */ addIncident(incident: IncidentRecord): RaceIncidents { return new RaceIncidents([...this.incidents, incident]); } /** * Get all incidents */ getAllIncidents(): IncidentRecord[] { return [...this.incidents]; } /** * Get total number of incidents */ getTotalCount(): number { return this.incidents.length; } /** * Get total penalty points from all incidents */ getTotalPenaltyPoints(): number { return this.incidents.reduce((total, incident) => total + (incident.penaltyPoints || 0), 0); } /** * Get incidents by type */ getIncidentsByType(type: IncidentType): IncidentRecord[] { return this.incidents.filter(incident => incident.type === type); } /** * Check if driver had any incidents */ hasIncidents(): boolean { return this.incidents.length > 0; } /** * Check if driver had a clean race (no incidents) */ isClean(): boolean { return this.incidents.length === 0; } // Removed getSeverityScore, getSummary, and getIncidentTypeLabel to eliminate static data in core equals(other: IValueObject): boolean { const otherIncidents = other.props; if (this.incidents.length !== otherIncidents.length) { return false; } // Sort both arrays and compare const sortedThis = [...this.incidents].sort((a, b) => a.lap - b.lap); const sortedOther = [...otherIncidents].sort((a, b) => a.lap - b.lap); return sortedThis.every((incident, index) => { const otherIncident = sortedOther[index]; return incident.type === otherIncident.type && incident.lap === otherIncident.lap && incident.description === otherIncident.description && incident.penaltyPoints === otherIncident.penaltyPoints; }); } }