import { MediaReference } from '@core/domain/media/MediaReference';
import { Driver } from '@core/racing/domain/entities/Driver';
import { faker } from '@faker-js/faker';
import { seedId } from './SeedIdHelper';

export interface DriverStats {
  rating: number;
  safetyRating: number;
  sportsmanshipRating: number;
  totalRaces: number;
  wins: number;
  podiums: number;
  dnfs: number;
  avgFinish: number;
  bestFinish: number;
  worstFinish: number;
  consistency: number;
  experienceLevel: 'beginner' | 'intermediate' | 'advanced' | 'veteran';
  overallRank: number | null;
}

export class RacingDriverFactory {
  constructor(
    private readonly driverCount: number,
    private readonly baseDate: Date,
    private readonly persistence: 'postgres' | 'inmemory' = 'inmemory',
  ) {}

  /**
   * Get deterministic MediaReference for a driver's avatar based on their ID
   * Uses hash % 3 to determine variant: 0 -> male, 1 -> female, 2 -> neutral
   */
  getDriverAvatarRef(driverId: string): MediaReference {
    // Deterministic selection based on driver ID hash
    const numericSuffixMatch = driverId.match(/(\d+)$/);
    let avatarVariant: 'male' | 'female' | 'neutral';

    if (numericSuffixMatch && numericSuffixMatch[1]) {
      const numericSuffix = parseInt(numericSuffixMatch[1], 10);
      const hashMod = numericSuffix % 3;
      if (hashMod === 0) {
        avatarVariant = 'male';
      } else if (hashMod === 1) {
        avatarVariant = 'female';
      } else {
        avatarVariant = 'neutral';
      }
    } else {
      // Fallback hash
      let hash = 0;
      for (let i = 0; i < driverId.length; i++) {
        hash = (hash * 31 + driverId.charCodeAt(i)) | 0;
      }
      const hashMod = Math.abs(hash) % 3;
      if (hashMod === 0) {
        avatarVariant = 'male';
      } else if (hashMod === 1) {
        avatarVariant = 'female';
      } else {
        avatarVariant = 'neutral';
      }
    }

    // Create system-default reference with avatar variant
    return MediaReference.createSystemDefault('avatar', avatarVariant);
  }

  create(): Driver[] {
    const countries = ['DE', 'NL', 'FR', 'GB', 'US', 'CA', 'SE', 'NO', 'IT', 'ES', 'AU', 'BR', 'JP', 'KR', 'RU', 'PL', 'CZ', 'HU', 'AT', 'CH'] as const;
    const categories = ['beginner', 'intermediate', 'advanced', 'pro', 'endurance', 'sprint'];

    return Array.from({ length: this.driverCount }, (_, idx) => {
      const i = idx + 1;

      // Vary bio presence: 80% have bio, 20% empty
      const hasBio = i % 5 !== 0;
      
      // Vary joined dates: some recent (new drivers), some old (veterans)
      let joinedAt: Date;
      if (i % 10 === 0) {
        // Very recent drivers (within last week)
        joinedAt = faker.date.recent({ days: 7, refDate: this.baseDate });
      } else if (i % 7 === 0) {
        // Recent drivers (within last month)
        joinedAt = faker.date.recent({ days: 30, refDate: this.baseDate });
      } else if (i % 5 === 0) {
        // Medium tenure (6-12 months ago)
        joinedAt = faker.date.between({
          from: new Date(this.baseDate.getTime() - 365 * 24 * 60 * 60 * 1000),
          to: new Date(this.baseDate.getTime() - 180 * 24 * 60 * 60 * 1000)
        });
      } else {
        // Veterans (1-2 years ago)
        joinedAt = faker.date.past({ years: 2, refDate: this.baseDate });
      }

      // Assign category - use all available categories
      const category = faker.helpers.arrayElement(categories);

      const driverId = seedId(`driver-${i}`, this.persistence);
      
      const driverData: {
        id: string;
        iracingId: string;
        name: string;
        country: string;
        bio?: string;
        joinedAt?: Date;
        category?: string;
        avatarRef: MediaReference;
      } = {
        id: driverId,
        iracingId: String(100000 + i),
        name: faker.person.fullName(),
        country: faker.helpers.arrayElement(countries),
        joinedAt,
        category,
        avatarRef: this.getDriverAvatarRef(driverId),
      };

      if (hasBio) {
        driverData.bio = faker.lorem.sentences(2);
      }

      return Driver.create(driverData);
    });
  }

  /**
   * Generate driver statistics for profile display
   * This would be stored in a separate stats table/service in production
   */
  generateDriverStats(drivers: Driver[]): Map<string, DriverStats> {
    const statsMap = new Map<string, DriverStats>();

    drivers.forEach((driver, idx) => {
      const i = idx + 1;
      
      // Determine experience level based on join date and index
      let experienceLevel: 'beginner' | 'intermediate' | 'advanced' | 'veteran';
      let totalRaces: number;
      let rating: number;
      let safetyRating: number;
      let sportsmanshipRating: number;

      if (i % 10 === 0) {
        // Very recent drivers
        experienceLevel = 'beginner';
        totalRaces = faker.number.int({ min: 1, max: 10 });
        rating = faker.number.int({ min: 800, max: 1200 });
        safetyRating = faker.number.int({ min: 60, max: 85 });
        sportsmanshipRating = Math.round(faker.number.float({ min: 3.0, max: 4.2 }) * 10) / 10;
      } else if (i % 7 === 0) {
        // Recent drivers
        experienceLevel = 'beginner';
        totalRaces = faker.number.int({ min: 5, max: 25 });
        rating = faker.number.int({ min: 1000, max: 1400 });
        safetyRating = faker.number.int({ min: 70, max: 90 });
        sportsmanshipRating = Math.round(faker.number.float({ min: 3.5, max: 4.5 }) * 10) / 10;
      } else if (i % 5 === 0) {
        // Medium tenure
        experienceLevel = 'intermediate';
        totalRaces = faker.number.int({ min: 20, max: 60 });
        rating = faker.number.int({ min: 1300, max: 1700 });
        safetyRating = faker.number.int({ min: 75, max: 95 });
        sportsmanshipRating = Math.round(faker.number.float({ min: 3.8, max: 4.8 }) * 10) / 10;
      } else if (i % 3 === 0) {
        // Advanced
        experienceLevel = 'advanced';
        totalRaces = faker.number.int({ min: 50, max: 120 });
        rating = faker.number.int({ min: 1600, max: 1900 });
        safetyRating = faker.number.int({ min: 80, max: 98 });
        sportsmanshipRating = Math.round(faker.number.float({ min: 4.0, max: 5.0 }) * 10) / 10;
      } else {
        // Veterans
        experienceLevel = 'veteran';
        totalRaces = faker.number.int({ min: 100, max: 200 });
        rating = faker.number.int({ min: 1700, max: 2000 });
        safetyRating = faker.number.int({ min: 85, max: 100 });
        sportsmanshipRating = Math.round(faker.number.float({ min: 4.2, max: 5.0 }) * 10) / 10;
      }

      // Calculate performance stats based on total races
      const winRate = experienceLevel === 'beginner' ? faker.number.float({ min: 0, max: 5, fractionDigits: 1 }) :
                      experienceLevel === 'intermediate' ? faker.number.float({ min: 2, max: 10, fractionDigits: 1 }) :
                      experienceLevel === 'advanced' ? faker.number.float({ min: 5, max: 15, fractionDigits: 1 }) :
                      faker.number.float({ min: 8, max: 20, fractionDigits: 1 });
      
      const podiumRate = experienceLevel === 'beginner' ? faker.number.float({ min: 5, max: 15, fractionDigits: 1 }) :
                         experienceLevel === 'intermediate' ? faker.number.float({ min: 10, max: 25, fractionDigits: 1 }) :
                         experienceLevel === 'advanced' ? faker.number.float({ min: 15, max: 35, fractionDigits: 1 }) :
                         faker.number.float({ min: 20, max: 45, fractionDigits: 1 });

      const wins = Math.round((winRate / 100) * totalRaces);
      const podiums = Math.round((podiumRate / 100) * totalRaces);
      const dnfs = Math.round(faker.number.float({ min: 0.05, max: 0.15, fractionDigits: 2 }) * totalRaces);
      
      const avgFinish = experienceLevel === 'beginner' ? faker.number.float({ min: 8, max: 15, fractionDigits: 1 }) :
                        experienceLevel === 'intermediate' ? faker.number.float({ min: 5, max: 10, fractionDigits: 1 }) :
                        experienceLevel === 'advanced' ? faker.number.float({ min: 3, max: 7, fractionDigits: 1 }) :
                        faker.number.float({ min: 2, max: 5, fractionDigits: 1 });

      const bestFinish = experienceLevel === 'beginner' ? faker.number.int({ min: 3, max: 8 }) :
                         experienceLevel === 'intermediate' ? faker.number.int({ min: 1, max: 5 }) :
                         faker.number.int({ min: 1, max: 3 });

      const worstFinish = experienceLevel === 'beginner' ? faker.number.int({ min: 12, max: 20 }) :
                          experienceLevel === 'intermediate' ? faker.number.int({ min: 8, max: 15 }) :
                          experienceLevel === 'advanced' ? faker.number.int({ min: 5, max: 12 }) :
                          faker.number.int({ min: 4, max: 10 });

      const consistency = experienceLevel === 'beginner' ? faker.number.float({ min: 40, max: 65, fractionDigits: 0 }) :
                          experienceLevel === 'intermediate' ? faker.number.float({ min: 60, max: 80, fractionDigits: 0 }) :
                          experienceLevel === 'advanced' ? faker.number.float({ min: 75, max: 90, fractionDigits: 0 }) :
                          faker.number.float({ min: 85, max: 98, fractionDigits: 0 });

      statsMap.set(driver.id.toString(), {
        rating,
        safetyRating,
        sportsmanshipRating,
        totalRaces,
        wins,
        podiums,
        dnfs,
        avgFinish: parseFloat(avgFinish.toFixed(1)),
        bestFinish,
        worstFinish,
        consistency: parseFloat(consistency.toFixed(1)),
        experienceLevel,
        overallRank: null, // Will be calculated after all stats are loaded
      });
    });

    return statsMap;
  }
}