import type { ValueObject } from '@core/shared/domain/ValueObject';

/**
 * Value Object: UserRating
 *
 * Multi-dimensional rating system for users covering:
 * - Driver skill: racing ability, lap times, consistency
 * - Admin competence: league management, event organization
 * - Steward fairness: protest handling, penalty consistency
 * - Trust score: reliability, sportsmanship, rule compliance
 * - Fairness score: clean racing, incident involvement
 */

export interface RatingDimension {
  value: number;      // Current rating value (0-100 scale)
  confidence: number; // Confidence level based on sample size (0-1)
  sampleSize: number; // Number of events contributing to this rating
  trend: 'rising' | 'stable' | 'falling';
  lastUpdated: Date;
}

export interface UserRatingProps {
  userId: string;
  driver: RatingDimension;
  admin: RatingDimension;
  steward: RatingDimension;
  trust: RatingDimension;
  fairness: RatingDimension;
  overallReputation: number;
  calculatorVersion?: string;
  createdAt: Date;
  updatedAt: Date;
}

const DEFAULT_DIMENSION: RatingDimension = {
  value: 50,
  confidence: 0,
  sampleSize: 0,
  trend: 'stable',
  lastUpdated: new Date(),
};

export class UserRating implements ValueObject<UserRatingProps> {
  readonly props: UserRatingProps;

  private constructor(props: UserRatingProps) {
    this.props = props;
  }

  get userId(): string {
    return this.props.userId;
  }

  get driver(): RatingDimension {
    return this.props.driver;
  }

  get admin(): RatingDimension {
    return this.props.admin;
  }

  get steward(): RatingDimension {
    return this.props.steward;
  }

  get trust(): RatingDimension {
    return this.props.trust;
  }

  get fairness(): RatingDimension {
    return this.props.fairness;
  }

  get overallReputation(): number {
    return this.props.overallReputation;
  }

  get createdAt(): Date {
    return this.props.createdAt;
  }

  get updatedAt(): Date {
    return this.props.updatedAt;
  }

  get calculatorVersion(): string | undefined {
    return this.props.calculatorVersion;
  }

  static create(userId: string): UserRating {
    if (!userId || userId.trim().length === 0) {
      throw new Error('UserRating userId is required');
    }

    const now = new Date();
    return new UserRating({
      userId,
      driver: { ...DEFAULT_DIMENSION, lastUpdated: now },
      admin: { ...DEFAULT_DIMENSION, lastUpdated: now },
      steward: { ...DEFAULT_DIMENSION, lastUpdated: now },
      trust: { ...DEFAULT_DIMENSION, lastUpdated: now },
      fairness: { ...DEFAULT_DIMENSION, lastUpdated: now },
      overallReputation: 50,
      calculatorVersion: '1.0',
      createdAt: now,
      updatedAt: now,
    });
  }

  static restore(props: UserRatingProps): UserRating {
    return new UserRating(props);
  }

  equals(other: ValueObject<UserRatingProps>): boolean {
    return this.props.userId === other.props.userId;
  }

  /**
   * Update driver rating based on race performance
   */
  updateDriverRating(
    newValue: number,
    weight: number = 1
  ): UserRating {
    const updated = this.updateDimension(this.driver, newValue, weight);
    return this.withUpdates({ driver: updated });
  }

  /**
   * Update admin rating based on league management feedback
   */
  updateAdminRating(
    newValue: number,
    weight: number = 1
  ): UserRating {
    const updated = this.updateDimension(this.admin, newValue, weight);
    return this.withUpdates({ admin: updated });
  }

  /**
   * Update steward rating based on protest handling feedback
   */
  updateStewardRating(
    newValue: number,
    weight: number = 1
  ): UserRating {
    const updated = this.updateDimension(this.steward, newValue, weight);
    return this.withUpdates({ steward: updated });
  }

  /**
   * Update trust score based on reliability and sportsmanship
   */
  updateTrustScore(
    newValue: number,
    weight: number = 1
  ): UserRating {
    const updated = this.updateDimension(this.trust, newValue, weight);
    return this.withUpdates({ trust: updated });
  }

  /**
   * Update fairness score based on clean racing incidents
   */
  updateFairnessScore(
    newValue: number,
    weight: number = 1
  ): UserRating {
    const updated = this.updateDimension(this.fairness, newValue, weight);
    return this.withUpdates({ fairness: updated });
  }

  /**
   * Calculate weighted overall reputation
   */
  calculateOverallReputation(): number {
    // Weight dimensions by confidence and importance
    const weights = {
      driver: 0.25 * this.driver.confidence,
      admin: 0.15 * this.admin.confidence,
      steward: 0.15 * this.steward.confidence,
      trust: 0.25 * this.trust.confidence,
      fairness: 0.20 * this.fairness.confidence,
    };

    const totalWeight = Object.values(weights).reduce((sum, w) => sum + w, 0);
    
    if (totalWeight === 0) {
      return 50; // Default when no ratings yet
    }

    const weightedSum =
      this.driver.value * weights.driver +
      this.admin.value * weights.admin +
      this.steward.value * weights.steward +
      this.trust.value * weights.trust +
      this.fairness.value * weights.fairness;

    return Math.round(weightedSum / totalWeight);
  }

  /**
   * Get rating tier for display
   */
  getDriverTier(): 'rookie' | 'amateur' | 'semi-pro' | 'pro' | 'elite' {
    if (this.driver.value >= 90) return 'elite';
    if (this.driver.value >= 75) return 'pro';
    if (this.driver.value >= 60) return 'semi-pro';
    if (this.driver.value >= 40) return 'amateur';
    return 'rookie';
  }

  /**
   * Get trust level for matchmaking
   */
  getTrustLevel(): 'unverified' | 'trusted' | 'highly-trusted' | 'community-leader' {
    if (this.trust.value >= 90 && this.trust.sampleSize >= 50) return 'community-leader';
    if (this.trust.value >= 75 && this.trust.sampleSize >= 20) return 'highly-trusted';
    if (this.trust.value >= 60 && this.trust.sampleSize >= 5) return 'trusted';
    return 'unverified';
  }

  /**
   * Check if user is eligible to be a steward
   */
  canBeSteward(): boolean {
    return (
      this.trust.value >= 70 &&
      this.fairness.value >= 70 &&
      this.trust.sampleSize >= 10
    );
  }

  /**
   * Check if user is eligible to be an admin
   */
  canBeAdmin(): boolean {
    return (
      this.trust.value >= 60 &&
      this.trust.sampleSize >= 5
    );
  }

  private updateDimension(
    dimension: RatingDimension,
    newValue: number,
    weight: number
  ): RatingDimension {
    const clampedValue = Math.max(0, Math.min(100, newValue));
    const newSampleSize = dimension.sampleSize + weight;
    
    // Exponential moving average with decay based on sample size
    const alpha = Math.min(0.3, 1 / (dimension.sampleSize + 1));
    const updatedValue = dimension.value * (1 - alpha) + clampedValue * alpha;

    // Calculate confidence (asymptotic to 1)
    const confidence = 1 - Math.exp(-newSampleSize / 20);

    // Determine trend
    const valueDiff = updatedValue - dimension.value;
    let trend: 'rising' | 'stable' | 'falling' = 'stable';
    if (valueDiff > 2) trend = 'rising';
    if (valueDiff < -2) trend = 'falling';

    return {
      value: Math.round(updatedValue * 10) / 10,
      confidence: Math.round(confidence * 100) / 100,
      sampleSize: newSampleSize,
      trend,
      lastUpdated: new Date(),
    };
  }

  private withUpdates(updates: Partial<UserRatingProps>): UserRating {
    const newRating = new UserRating({
      ...this.props,
      ...updates,
      updatedAt: new Date(),
    });
     
    // Recalculate overall reputation
    return new UserRating({
      ...newRating.props,
      overallReputation: newRating.calculateOverallReputation(),
    });
  }
}