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 {
  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<void> {
    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<void> {
    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<void> {
    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<void> {
    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);
  }
}