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

/**
 * Value Object: TeamRating
 *
 * Multi-dimensional rating system for teams covering:
 * - Driving: racing ability, performance, consistency
 * - AdminTrust: reliability, leadership, community contribution
 */

export interface TeamRatingDimension {
  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 TeamRatingProps {
  teamId: string;
  driving: TeamRatingDimension;
  adminTrust: TeamRatingDimension;
  overall: number;
  calculatorVersion?: string;
  createdAt: Date;
  updatedAt: Date;
}

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

export class TeamRating implements ValueObject<TeamRatingProps> {
  readonly props: TeamRatingProps;

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

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

  get driving(): TeamRatingDimension {
    return this.props.driving;
  }

  get adminTrust(): TeamRatingDimension {
    return this.props.adminTrust;
  }

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

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

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

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

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

    const now = new Date();
    return new TeamRating({
      teamId,
      driving: { ...DEFAULT_DIMENSION, lastUpdated: now },
      adminTrust: { ...DEFAULT_DIMENSION, lastUpdated: now },
      overall: 50,
      calculatorVersion: '1.0',
      createdAt: now,
      updatedAt: now,
    });
  }

  static restore(props: TeamRatingProps): TeamRating {
    return new TeamRating(props);
  }

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

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

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

  /**
   * Calculate weighted overall rating
   */
  calculateOverall(): number {
    // Weight dimensions by confidence
    const weights = {
      driving: 0.7 * this.driving.confidence,
      adminTrust: 0.3 * this.adminTrust.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.driving.value * weights.driving +
      this.adminTrust.value * weights.adminTrust;

    return Math.round(weightedSum / totalWeight);
  }

  private updateDimension(
    dimension: TeamRatingDimension,
    newValue: number,
    weight: number
  ): TeamRatingDimension {
    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<TeamRatingProps>): TeamRating {
    const newRating = new TeamRating({
      ...this.props,
      ...updates,
      updatedAt: new Date(),
    });
     
    // Recalculate overall
    return new TeamRating({
      ...newRating.props,
      overall: newRating.calculateOverall(),
    });
  }
}