import { TeamRatingEvent } from '../entities/TeamRatingEvent';
import { TeamRatingValue } from '../value-objects/TeamRatingValue';
import { TeamRatingDimensionKey } from '../value-objects/TeamRatingDimensionKey';

export interface TeamRatingSnapshot {
  teamId: string;
  driving: TeamRatingValue;
  adminTrust: TeamRatingValue;
  overall: number; // Calculated overall rating
  lastUpdated: Date;
  eventCount: number;
}

/**
 * Domain Service: TeamRatingSnapshotCalculator
 *
 * Calculates team rating snapshots from event ledgers.
 * Mirrors the user RatingSnapshotCalculator pattern.
 *
 * Pure domain logic - no persistence concerns.
 */
export class TeamRatingSnapshotCalculator {
  /**
   * Calculate current team rating snapshot from all events.
   * 
   * @param teamId - The team ID to calculate for
   * @param events - All rating events for the team
   * @returns TeamRatingSnapshot with current ratings
   */
  static calculate(teamId: string, events: TeamRatingEvent[]): TeamRatingSnapshot {
    // Start with default ratings (50 for each dimension)
    const defaultRating = 50;

    if (events.length === 0) {
      return {
        teamId,
        driving: TeamRatingValue.create(defaultRating),
        adminTrust: TeamRatingValue.create(defaultRating),
        overall: defaultRating,
        lastUpdated: new Date(),
        eventCount: 0,
      };
    }

    // Group events by dimension
    const eventsByDimension = events.reduce((acc, event) => {
      const key = event.dimension.value;
      if (!acc[key]) {
        acc[key] = [];
      }
      acc[key].push(event);
      return acc;
    }, {} as Record<string, TeamRatingEvent[]>);

    // Calculate each dimension
    const dimensionRatings: Record<string, number> = {};

    for (const [dimensionKey, dimensionEvents] of Object.entries(eventsByDimension)) {
      const totalWeight = dimensionEvents.reduce((sum, event) => {
        return sum + (event.weight || 1);
      }, 0);

      const weightedSum = dimensionEvents.reduce((sum, event) => {
        return sum + (event.delta.value * (event.weight || 1));
      }, 0);

      // Normalize and add to base rating
      const normalizedDelta = weightedSum / totalWeight;
      dimensionRatings[dimensionKey] = Math.max(0, Math.min(100, defaultRating + normalizedDelta));
    }

    const drivingRating = dimensionRatings['driving'] ?? defaultRating;
    const adminTrustRating = dimensionRatings['adminTrust'] ?? defaultRating;

    // Calculate overall as weighted average
    const overall = (drivingRating * 0.7 + adminTrustRating * 0.3);

    // Find latest event date
    const lastUpdated = events.reduce((latest, event) => {
      return event.occurredAt > latest ? event.occurredAt : latest;
    }, new Date(0));

    return {
      teamId,
      driving: TeamRatingValue.create(drivingRating),
      adminTrust: TeamRatingValue.create(adminTrustRating),
      overall: Math.round(overall * 10) / 10, // Round to 1 decimal
      lastUpdated,
      eventCount: events.length,
    };
  }

  /**
   * Calculate rating change for a specific dimension from events.
   * 
   * @param dimension - The dimension to calculate for
   * @param events - Events to calculate from
   * @returns Net change value
   */
  static calculateDimensionChange(
    dimension: TeamRatingDimensionKey,
    events: TeamRatingEvent[]
  ): number {
    const filtered = events.filter(e => e.dimension.equals(dimension));
    
    if (filtered.length === 0) return 0;

    const totalWeight = filtered.reduce((sum, event) => {
      return sum + (event.weight || 1);
    }, 0);

    const weightedSum = filtered.reduce((sum, event) => {
      return sum + (event.delta.value * (event.weight || 1));
    }, 0);

    return weightedSum / totalWeight;
  }

  /**
   * Calculate rating change over a time window.
   * 
   * @param teamId - The team ID
   * @param events - All events
   * @param from - Start date
   * @param to - End date
   * @returns Snapshot of ratings at the end of the window
   */
  static calculateOverWindow(
    teamId: string,
    events: TeamRatingEvent[],
    from: Date,
    to: Date
  ): TeamRatingSnapshot {
    const windowEvents = events.filter(e => 
      e.occurredAt >= from && e.occurredAt <= to
    );

    return this.calculate(teamId, windowEvents);
  }

  /**
   * Calculate rating change between two snapshots.
   * 
   * @param before - Snapshot before changes
   * @param after - Snapshot after changes
   * @returns Object with change values
   */
  static calculateDelta(
    before: TeamRatingSnapshot,
    after: TeamRatingSnapshot
  ): {
    driving: number;
    adminTrust: number;
    overall: number;
  } {
    return {
      driving: after.driving.value - before.driving.value,
      adminTrust: after.adminTrust.value - before.adminTrust.value,
      overall: after.overall - before.overall,
    };
  }
}