import { ResultWithIncidents } from '../../domain/entities/ResultWithIncidents';
import { RaceIncidents, type IncidentRecord, type IncidentType } from '../../domain/value-objects/RaceIncidents';

/**
 * Enhanced race result generator with detailed incident types
 */
export class RaceResultGeneratorWithIncidents {
  /**
   * Generate realistic race results with detailed incidents
   */
  static generateRaceResults(
    raceId: string,
    driverIds: string[],
    driverRatings: Map<string, number>
  ): ResultWithIncidents[] {
    // Create driver performance data
    const driverPerformances = driverIds.map(driverId => ({
      driverId,
      rating: driverRatings.get(driverId) ?? 1500, // Default rating
      randomFactor: Math.random() - 0.5, // -0.5 to +0.5 randomization
    }));

    // Sort by performance (rating + randomization)
    driverPerformances.sort((a, b) => {
      const perfA = a.rating + (a.randomFactor * 200); // ±100 rating points randomization
      const perfB = b.rating + (b.randomFactor * 200);
      return perfB - perfA; // Higher performance first
    });

    // Generate qualifying results for start positions (similar but different from race results)
    const qualiPerformances = driverPerformances.map(p => ({
      ...p,
      randomFactor: Math.random() - 0.5, // New randomization for quali
    }));
    qualiPerformances.sort((a, b) => {
      const perfA = a.rating + (a.randomFactor * 150);
      const perfB = b.rating + (b.randomFactor * 150);
      return perfB - perfA;
    });

    // Generate results
    const results: ResultWithIncidents[] = [];
    for (let i = 0; i < driverPerformances.length; i++) {
      const { driverId } = driverPerformances[i];
      const position = i + 1;
      const startPosition = qualiPerformances.findIndex(p => p.driverId === driverId) + 1;

      // Generate realistic lap times (90-120 seconds for a lap)
      const baseLapTime = 90000 + Math.random() * 30000;
      const positionBonus = (position - 1) * 500; // Winners are faster
      const fastestLap = Math.round(baseLapTime + positionBonus + Math.random() * 5000);

      // Generate detailed incidents
      const incidents = this.generateDetailedIncidents(position, driverPerformances.length);

      results.push(
        ResultWithIncidents.create({
          id: `${raceId}-${driverId}`,
          raceId,
          driverId,
          position,
          startPosition,
          fastestLap,
          incidents,
        })
      );
    }

    return results;
  }

  /**
   * Generate detailed incidents with specific types and severity
   */
  private static generateDetailedIncidents(position: number, totalDrivers: number): RaceIncidents {
    // Base probability increases for lower positions (more aggressive driving)
    const baseProbability = Math.min(0.85, position / totalDrivers + 0.1);

    // Add some randomness
    const randomFactor = Math.random();

    if (randomFactor > baseProbability) {
      // Clean race
      return new RaceIncidents();
    }

    // Determine number of incidents based on position and severity
    const severityRoll = Math.random();
    let incidentCount: number;

    if (severityRoll < 0.5) {
      incidentCount = 1; // Minor incident
    } else if (severityRoll < 0.8) {
      incidentCount = 2; // Moderate incident
    } else if (severityRoll < 0.95) {
      incidentCount = 3; // Major incident
    } else {
      incidentCount = Math.floor(Math.random() * 2) + 3; // 3-4 incidents (severe)
    }

    // Generate specific incidents
    const incidents: IncidentRecord[] = [];
    for (let i = 0; i < incidentCount; i++) {
      const incidentType = this.selectIncidentType(position, totalDrivers, i);
      const lap = this.selectIncidentLap(i + 1, incidentCount);

      incidents.push({
        type: incidentType,
        lap,
        description: this.generateIncidentDescription(incidentType),
        penaltyPoints: this.getPenaltyPoints(incidentType),
      });
    }

    return new RaceIncidents(incidents);
  }

  /**
   * Select appropriate incident type based on context
   */
  private static selectIncidentType(position: number, totalDrivers: number, incidentIndex: number): IncidentType {
    // Different incident types have different probabilities
    const incidentProbabilities: Array<{ type: IncidentType; weight: number }> = [
      { type: 'track_limits', weight: 40 },      // Most common
      { type: 'contact', weight: 25 },           // Common in traffic
      { type: 'unsafe_rejoin', weight: 15 },     // Dangerous
      { type: 'aggressive_driving', weight: 10 }, // Less common
      { type: 'collision', weight: 5 },          // Rare
      { type: 'spin', weight: 4 },               // Rare
      { type: 'false_start', weight: 1 },        // Very rare in race
    ];

    // Adjust weights based on position (lower positions more likely to have contact/aggressive driving)
    if (position > totalDrivers * 0.7) { // Bottom 30%
      incidentProbabilities.find(p => p.type === 'contact')!.weight += 10;
      incidentProbabilities.find(p => p.type === 'aggressive_driving')!.weight += 5;
    }

    // Select based on weights
    const totalWeight = incidentProbabilities.reduce((sum, p) => sum + p.weight, 0);
    let random = Math.random() * totalWeight;

    for (const { type, weight } of incidentProbabilities) {
      random -= weight;
      if (random <= 0) {
        return type;
      }
    }

    return 'track_limits'; // Fallback
  }

  /**
   * Select appropriate lap for incident
   */
  private static selectIncidentLap(incidentNumber: number, totalIncidents: number): number {
    // Spread incidents throughout the race
    const raceLaps = 20; // Assume 20 lap race
    const lapRanges = [
      { min: 1, max: 5 },    // Early race
      { min: 6, max: 12 },   // Mid race
      { min: 13, max: 20 },  // Late race
    ];

    // Distribute incidents across race phases
    const phaseIndex = Math.min(incidentNumber - 1, lapRanges.length - 1);
    const range = lapRanges[phaseIndex];

    return Math.floor(Math.random() * (range.max - range.min + 1)) + range.min;
  }

  /**
   * Generate human-readable description for incident
   */
  private static generateIncidentDescription(type: IncidentType): string {
    const descriptions: Record<IncidentType, string[]> = {
      track_limits: [
        'Went off track at corner exit',
        'Cut corner to maintain position',
        'Ran wide under braking',
        'Off-track excursion gaining advantage',
      ],
      contact: [
        'Light contact while defending position',
        'Side-by-side contact into corner',
        'Rear-end contact under braking',
        'Wheel-to-wheel contact',
      ],
      unsafe_rejoin: [
        'Unsafe rejoin across track',
        'Rejoined directly into racing line',
        'Failed to check mirrors before rejoining',
        'Forced another driver off track on rejoin',
      ],
      aggressive_driving: [
        'Multiple defensive moves under braking',
        'Moved under braking three times',
        'Aggressive defending forcing driver wide',
        'Persistent blocking maneuvers',
      ],
      collision: [
        'Collision involving multiple cars',
        'Major contact causing safety car',
        'Chain reaction collision',
        'Heavy impact collision',
      ],
      spin: [
        'Lost control and spun',
        'Oversteer spin into gravel',
        'Spin following contact',
        'Lost rear grip and spun',
      ],
      false_start: [
        'Jumped start before green flag',
        'Early launch from grid',
        'Premature start',
      ],
      mechanical: [
        'Engine failure',
        'Gearbox issue',
        'Brake failure',
        'Suspension damage',
      ],
      other: [
        'Unspecified incident',
        'Race incident',
        'Driving infraction',
      ],
    };

    const options = descriptions[type] || descriptions.other;
    return options[Math.floor(Math.random() * options.length)];
  }

  /**
   * Get penalty points for incident type
   */
  private static getPenaltyPoints(type: IncidentType): number {
    const penalties: Record<IncidentType, number> = {
      track_limits: 0,        // Usually warning only
      contact: 2,             // Light penalty
      unsafe_rejoin: 3,       // Moderate penalty
      aggressive_driving: 2,  // Light penalty
      false_start: 5,         // Heavy penalty
      collision: 5,           // Heavy penalty
      spin: 0,                // Usually no penalty if no contact
      mechanical: 0,          // Not driver fault
      other: 2,               // Default penalty
    };
    return penalties[type];
  }

  /**
   * Get incident description for display
   */
  static getIncidentDescription(incidents: RaceIncidents): string {
    return incidents.getSummary();
  }

  /**
   * Calculate incident penalty points for standings
   */
  static getIncidentPenaltyPoints(incidents: RaceIncidents): number {
    return incidents.getTotalPenaltyPoints();
  }
}