import type { IDomainService } from '@core/shared/domain'; import type { IUserRatingRepository } from '../repositories/IUserRatingRepository'; import { UserRating } from '../value-objects/UserRating'; /** * Domain Service: RatingUpdateService * * Handles updating user ratings based on various events and performance metrics. * Centralizes rating calculation logic and ensures consistency across the system. */ export class RatingUpdateService implements IDomainService { readonly serviceName = 'RatingUpdateService'; constructor( private readonly userRatingRepository: IUserRatingRepository ) {} /** * Update driver ratings after race completion */ async updateDriverRatingsAfterRace( driverResults: Array<{ driverId: string; position: number; totalDrivers: number; incidents: number; startPosition: number; }> ): Promise { for (const result of driverResults) { await this.updateDriverRating(result); } } /** * Update individual driver rating based on race result */ private async updateDriverRating(result: { driverId: string; position: number; totalDrivers: number; incidents: number; startPosition: number; }): Promise { const { driverId, position, totalDrivers, incidents, startPosition } = result; // Get or create user rating let userRating = await this.userRatingRepository.findByUserId(driverId); if (!userRating) { userRating = UserRating.create(driverId); } // Calculate performance score (0-100) const performanceScore = this.calculatePerformanceScore(position, totalDrivers, startPosition); // Calculate fairness score based on incidents (lower incidents = higher fairness) const fairnessScore = this.calculateFairnessScore(incidents, totalDrivers); // Update ratings const updatedRating = userRating .updateDriverRating(performanceScore) .updateFairnessScore(fairnessScore); // Save updated rating await this.userRatingRepository.save(updatedRating); } /** * Calculate performance score based on finishing position and field strength */ private calculatePerformanceScore( position: number, totalDrivers: number, startPosition: number ): number { // Base score from finishing position (reverse percentile) const positionScore = ((totalDrivers - position + 1) / totalDrivers) * 100; // Bonus for positions gained const positionsGained = startPosition - position; const gainBonus = Math.max(0, positionsGained * 2); // 2 points per position gained // Field strength adjustment (harder fields give higher scores for same position) const fieldStrengthMultiplier = 0.8 + (totalDrivers / 50); // Max 1.0 for 30+ drivers const rawScore = (positionScore + gainBonus) * fieldStrengthMultiplier; // Clamp to 0-100 range return Math.max(0, Math.min(100, rawScore)); } /** * Calculate fairness score based on incident involvement */ private calculateFairnessScore(incidents: number, totalDrivers: number): number { // Base fairness score (100 = perfect, 0 = terrible) let fairnessScore = 100; // Deduct points for incidents fairnessScore -= incidents * 15; // 15 points per incident // Additional deduction for high incident rate relative to field const incidentRate = incidents / totalDrivers; if (incidentRate > 0.5) { fairnessScore -= 20; // Heavy penalty for being involved in many incidents } // Clamp to 0-100 range return Math.max(0, Math.min(100, fairnessScore)); } /** * Update trust score based on sportsmanship actions */ async updateTrustScore(driverId: string, trustChange: number): Promise { let userRating = await this.userRatingRepository.findByUserId(driverId); if (!userRating) { userRating = UserRating.create(driverId); } // Convert trust change (-50 to +50) to 0-100 scale const currentTrust = userRating.trust.value; const newTrustValue = Math.max(0, Math.min(100, currentTrust + trustChange)); const updatedRating = userRating.updateTrustScore(newTrustValue); await this.userRatingRepository.save(updatedRating); } /** * Update steward rating based on protest handling quality */ async updateStewardRating(stewardId: string, ratingChange: number): Promise { let userRating = await this.userRatingRepository.findByUserId(stewardId); if (!userRating) { userRating = UserRating.create(stewardId); } const currentRating = userRating.steward.value; const newRatingValue = Math.max(0, Math.min(100, currentRating + ratingChange)); const updatedRating = userRating.updateStewardRating(newRatingValue); await this.userRatingRepository.save(updatedRating); } }