import type { IDomainService } from '@core/shared/domain';
import type { IUserRatingRepository } from '../repositories/IUserRatingRepository';
import type { IRatingEventRepository } from '../repositories/IRatingEventRepository';
import { RatingEventFactory } from './RatingEventFactory';
import { RatingSnapshotCalculator } from './RatingSnapshotCalculator';
import { RatingEvent } from '../entities/RatingEvent';
import { RatingEventId } from '../value-objects/RatingEventId';
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
import { RatingDelta } from '../value-objects/RatingDelta';

/**
 * Domain Service: RatingUpdateService
 *
 * Handles updating user ratings based on various events and performance metrics.
 * Centralizes rating calculation logic and ensures consistency across the system.
 *
 * EVOLVED (Slice 7): Now uses event-driven approach with ledger pattern.
 * Records rating events and recomputes snapshots for transparency and auditability.
 */
export class RatingUpdateService implements IDomainService {
  readonly serviceName = 'RatingUpdateService';
  
  constructor(
    private readonly userRatingRepository: IUserRatingRepository,
    private readonly ratingEventRepository: IRatingEventRepository
  ) {}

  /**
   * Record race rating events and update snapshots (NEW LEDGER APPROACH)
   * Replaces direct rating updates with event recording + snapshot recomputation
   */
  async recordRaceRatingEvents(raceId: string, raceResults: Array<{
    userId: string;
    startPos: number;
    finishPos: number;
    incidents: number;
    status: 'finished' | 'dnf' | 'dns' | 'dsq' | 'afk';
    sof?: number;
  }>): Promise<{ success: boolean; eventsCreated: number; driversUpdated: string[] }> {
    try {
      // Use factory to create rating events from race results
      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace({
        raceId,
        results: raceResults,
      });

      let totalEvents = 0;
      const driversUpdated: string[] = [];

      // Process each user's events
      for (const [userId, events] of eventsByUser) {
        if (events.length === 0) continue;

        // Save all events to ledger
        for (const event of events) {
          await this.ratingEventRepository.save(event);
          totalEvents++;
        }

        // Recompute snapshot from all events for this user
        const allEvents = await this.ratingEventRepository.getAllByUserId(userId);
        const snapshot = RatingSnapshotCalculator.calculate(userId, allEvents);
        await this.userRatingRepository.save(snapshot);

        driversUpdated.push(userId);
      }

      return {
        success: true,
        eventsCreated: totalEvents,
        driversUpdated,
      };
    } catch (error) {
      console.error('[RatingUpdateService] Failed to record race rating events:', error);
      return {
        success: false,
        eventsCreated: 0,
        driversUpdated: [],
      };
    }
  }

  /**
   * Update driver ratings after race completion (BACKWARD COMPATIBLE)
   * Still supported but now delegates to event-based approach internally
   */
  async updateDriverRatingsAfterRace(
    driverResults: Array<{
      driverId: string;
      position: number;
      totalDrivers: number;
      incidents: number;
      startPosition: number;
    }>
  ): Promise<void> {
    // Convert to new format and use event-based approach
    const raceResults = driverResults.map(result => ({
      userId: result.driverId,
      startPos: result.startPosition,
      finishPos: result.position,
      incidents: result.incidents,
      status: 'finished' as const,
    }));

    // Generate a synthetic race ID for backward compatibility
    const raceId = `backward-compat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

    const result = await this.recordRaceRatingEvents(raceId, raceResults);
    
    if (!result.success) {
      throw new Error('Failed to update ratings via event system');
    }
  }

  /**
   * Update individual driver rating based on race result (LEGACY - DEPRECATED)
   * Kept for backward compatibility but now uses event-based approach
   */
  private async updateDriverRating(result: {
    driverId: string;
    position: number;
    totalDrivers: number;
    incidents: number;
    startPosition: number;
  }): Promise<void> {
    // Delegate to new event-based approach
    await this.updateDriverRatingsAfterRace([result]);
  }

  /**
   * Update trust score based on sportsmanship actions (USES LEDGER)
   */
  async updateTrustScore(driverId: string, trustChange: number): Promise<void> {
    // Create trust-related rating event using manual event creation
    const now = new Date();
    const event = RatingEvent.create({
      id: RatingEventId.generate(),
      userId: driverId,
      dimension: RatingDimensionKey.create('adminTrust'),
      delta: RatingDelta.create(trustChange),
      weight: 1,
      occurredAt: now,
      createdAt: now,
      source: { type: 'manualAdjustment', id: `trust-${now.getTime()}` },
      reason: {
        code: trustChange > 0 ? 'TRUST_BONUS' : 'TRUST_PENALTY',
        summary: trustChange > 0 ? 'Positive sportsmanship' : 'Negative sportsmanship',
        details: { trustChange },
      },
      visibility: { public: true, redactedFields: [] },
      version: 1,
    });

    // Save event
    await this.ratingEventRepository.save(event);

    // Recompute snapshot
    const allEvents = await this.ratingEventRepository.getAllByUserId(driverId);
    const snapshot = RatingSnapshotCalculator.calculate(driverId, allEvents);
    await this.userRatingRepository.save(snapshot);
  }

  /**
   * Update steward rating based on protest handling quality (USES LEDGER)
   */
  async updateStewardRating(stewardId: string, ratingChange: number): Promise<void> {
    // Create steward-related rating event using manual event creation
    const now = new Date();
    const event = RatingEvent.create({
      id: RatingEventId.generate(),
      userId: stewardId,
      dimension: RatingDimensionKey.create('adminTrust'),
      delta: RatingDelta.create(ratingChange),
      weight: 1,
      occurredAt: now,
      createdAt: now,
      source: { type: 'manualAdjustment', id: `steward-${now.getTime()}` },
      reason: {
        code: ratingChange > 0 ? 'STEWARD_BONUS' : 'STEWARD_PENALTY',
        summary: ratingChange > 0 ? 'Good protest handling' : 'Poor protest handling',
        details: { ratingChange },
      },
      visibility: { public: true, redactedFields: [] },
      version: 1,
    });

    // Save event
    await this.ratingEventRepository.save(event);

    // Recompute snapshot
    const allEvents = await this.ratingEventRepository.getAllByUserId(stewardId);
    const snapshot = RatingSnapshotCalculator.calculate(stewardId, allEvents);
    await this.userRatingRepository.save(snapshot);
  }

  /**
   * Calculate performance score based on finishing position and field strength
   * (Utility method kept for reference, but now handled by RatingEventFactory)
   */
  private calculatePerformanceScore(
    position: number,
    totalDrivers: number,
    startPosition: number
  ): number {
    const positionScore = ((totalDrivers - position + 1) / totalDrivers) * 100;
    const positionsGained = startPosition - position;
    const gainBonus = Math.max(0, positionsGained * 2);
    const fieldStrengthMultiplier = 0.8 + (totalDrivers / 50);
    const rawScore = (positionScore + gainBonus) * fieldStrengthMultiplier;
    return Math.max(0, Math.min(100, rawScore));
  }

  /**
   * Calculate fairness score based on incident involvement
   * (Utility method kept for reference, but now handled by RatingEventFactory)
   */
  private calculateFairnessScore(incidents: number, totalDrivers: number): number {
    let fairnessScore = 100;
    fairnessScore -= incidents * 15;
    const incidentRate = incidents / totalDrivers;
    if (incidentRate > 0.5) {
      fairnessScore -= 20;
    }
    return Math.max(0, Math.min(100, fairnessScore));
  }
}