import { MediaReference } from '@core/domain/media/MediaReference';
import { Driver } from '@core/racing/domain/entities/Driver';
import { League } from '@core/racing/domain/entities/League';
import { Team } from '@core/racing/domain/entities/Team';
import type { TeamJoinRequest, TeamMembership } from '@core/racing/domain/types/TeamMembership';
import { faker } from '@faker-js/faker';
import { seedId } from './SeedIdHelper';

export interface TeamStats {
  performanceLevel: 'beginner' | 'intermediate' | 'advanced' | 'pro';
  specialization: 'endurance' | 'sprint' | 'mixed';
  region: string;
  languages: string[];
  totalWins: number;
  totalRaces: number;
  rating: number;
  category?: string;
}

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

  createTeams(drivers: Driver[], leagues: League[]): Team[] {
    const teamCount = 50; // Increased from 15 to 50

    return Array.from({ length: teamCount }, (_, idx) => {
      const i = idx + 1;
      const owner = faker.helpers.arrayElement(drivers);
      const teamLeagues = faker.helpers.arrayElements(
        leagues.map((l) => l.id.toString()),
        { min: 0, max: 3 },
      );

      // 30-50% of teams are recruiting
      const isRecruiting = faker.datatype.boolean({ probability: 0.4 });

      const teamId = seedId(`team-${i}`, this.persistence);

      const racingNames = [
        'Apex Performance', 'Velocity Racing', 'Zenith Motorsport', 'Quantum Racing',
        'Ignition Racing', 'Precision Dynamics', 'Overdrive Motorsport', 'Apex Predators',
        'Gridline Racing', 'Shift Point Motorsport', 'Redline Performance', 'Apex Legends',
        'Circuit Breakers', 'Full Throttle Racing', 'Gearhead Motorsport', 'Piston Cup Racing',
        'Turbo Titans', 'Nitro Knights', 'Velocity Vanguards', 'Mach One Racing',
        'Apex Alliance', 'Elite Endurance', 'Sprint Specialists', 'Grand Prix Group',
        'Podium Pursuit', 'Victory Vibe', 'Championship Chase', 'Racing Renegades',
        'Track Titans', 'Asphalt Assassins', 'Speed Syndicate', 'Fast Lane Force',
        'Apex Architects', 'Velocity Visionaries', 'Zenith Zephyrs', 'Quantum Quicksilver',
        'Ignition Iron', 'Precision Pilots', 'Overdrive Outlaws', 'Apex Aces',
        'Gridline Guardians', 'Shift Point Sentinels', 'Redline Rebels', 'Apex Avengers',
        'Circuit Crusaders', 'Full Throttle Falcons', 'Gearhead Giants', 'Piston Cup Pros',
        'Turbo Tigers', 'Nitro Ninjas'
      ];

      const name = racingNames[(i - 1) % racingNames.length]!;

      return Team.create({
        id: teamId,
        name: name,
        tag: name.split(' ').map(w => w[0]).join('').toUpperCase().substring(0, 4),
        description: faker.lorem.sentences(2),
        ownerId: owner.id,
        leagues: teamLeagues,
        isRecruiting,
        createdAt: faker.date.past({ years: 2, refDate: this.baseDate }),
        logoRef: MediaReference.createGenerated(`team-${teamId}`),
      });
    });
  }

  createTeamMemberships(drivers: Driver[], teams: Team[]): TeamMembership[] {
    const memberships: TeamMembership[] = [];
    const usedDrivers = new Set<string>();

    teams.forEach((team, teamIndex) => {
      const availableDrivers = drivers.filter(d => !usedDrivers.has(d.id.toString()) && d.id.toString() !== team.ownerId.toString());

      // Create varied team compositions
      let memberCount: number;
      let hasManager: boolean;

      if (teamIndex % 5 === 0) {
        // Solo teams (just owner)
        memberCount = 0;
        hasManager = false;
      } else if (teamIndex % 5 === 1) {
        // Small teams (2-3 members)
        memberCount = faker.number.int({ min: 1, max: 2 });
        hasManager = faker.datatype.boolean();
      } else if (teamIndex % 5 === 2) {
        // Medium teams (3-5 members)
        memberCount = faker.number.int({ min: 2, max: 4 });
        hasManager = true;
      } else if (teamIndex % 5 === 3) {
        // Large teams (5-7 members)
        memberCount = faker.number.int({ min: 4, max: 6 });
        hasManager = true;
      } else {
        // Mixed - sometimes with manager, sometimes without
        memberCount = faker.number.int({ min: 1, max: 5 });
        hasManager = faker.datatype.boolean();
      }

      const members = faker.helpers.arrayElements(availableDrivers, memberCount);

      // Add owner
      memberships.push({
        teamId: team.id.toString(),
        driverId: team.ownerId.toString(),
        role: 'owner',
        status: 'active',
        joinedAt: faker.date.past({ years: 1, refDate: team.createdAt.toDate() }),
      });
      usedDrivers.add(team.ownerId.toString());

      // Add manager if needed
      if (hasManager && members.length > 0) {
        const managerIndex = faker.number.int({ min: 0, max: members.length - 1 });
        const manager = members[managerIndex]!;
        memberships.push({
          teamId: team.id.toString(),
          driverId: manager.id.toString(),
          role: 'manager',
          status: 'active',
          joinedAt: faker.date.past({ years: 1, refDate: team.createdAt.toDate() }),
        });
        usedDrivers.add(manager.id.toString());
      }

      // Add remaining members as drivers
      members.forEach((driver) => {
        if (!usedDrivers.has(driver.id.toString())) {
          memberships.push({
            teamId: team.id.toString(),
            driverId: driver.id.toString(),
            role: 'driver',
            status: 'active',
            joinedAt: faker.date.past({ years: 1, refDate: team.createdAt.toDate() }),
          });
          usedDrivers.add(driver.id.toString());
        }
      });
    });

    return memberships;
  }

  createTeamJoinRequests(
    drivers: Driver[],
    teams: Team[],
    teamMemberships: TeamMembership[],
  ): TeamJoinRequest[] {
    const membershipIds = new Set<string>(teamMemberships.map(m => `${m.teamId}:${m.driverId}`));
    const requests: TeamJoinRequest[] = [];

    const addRequest = (request: TeamJoinRequest): void => {
      requests.push(request);
    };

    const team1 = teams[0];
    if (team1) {
      const candidateDriverIds = drivers
        .map(d => d.id.toString())
        .filter(driverId => !membershipIds.has(`${team1.id.toString()}:${driverId}`))
        .slice(0, 8);

      candidateDriverIds.forEach((driverId, idx) => {
        addRequest({
          id: seedId(`team-join-${team1.id.toString()}-${driverId}`, this.persistence),
          teamId: team1.id.toString(),
          driverId,
          requestedAt: this.addDays(this.baseDate, -(5 + idx)),
          ...(idx % 3 === 0
            ? { message: 'Can I join as a substitute driver for endurance events?' }
            : idx % 3 === 2
              ? { message: '' }
              : {}),
        });
      });

      // Conflict edge case: owner submits a join request to own team.
      addRequest({
        id: seedId(`team-join-${team1.id.toString()}-${team1.ownerId.toString()}-conflict`, this.persistence),
        teamId: team1.id.toString(),
        driverId: team1.ownerId.toString(),
        requestedAt: this.addDays(this.baseDate, -1),
        message: 'Testing edge case: owner submitted join request.',
      });
    }

    const team3 = teams[2];
    if (team3 && drivers[0]) {
      const driverId = drivers[0].id.toString();
      addRequest({
        id: seedId('dup-team-join-req-1', this.persistence),
        teamId: team3.id.toString(),
        driverId,
        requestedAt: this.addDays(this.baseDate, -10),
        message: 'First request message (will be overwritten).',
      });

      addRequest({
        id: seedId('dup-team-join-req-1', this.persistence),
        teamId: team3.id.toString(),
        driverId,
        requestedAt: this.addDays(this.baseDate, -9),
        message: 'Updated request message (duplicate id).',
      });
    }

    return requests;
  }

  /**
   * Generate team statistics and metadata for display
   * This would be stored in a separate stats table/service in production
   */
  generateTeamStats(teams: Team[]): Map<string, TeamStats> {
    const statsMap = new Map<string, TeamStats>();

    // Available regions (using country codes for flags)
    const regions = ['DE', 'GB', 'US', 'FR', 'IT', 'ES', 'BR', 'JP', 'AU', 'NL', 'BE', 'AT', 'CH', 'SE', 'NO', 'FI', 'DK', 'PL', 'CZ', 'HU'];

    // Available languages
    const allLanguages = ['English', 'German', 'French', 'Spanish', 'Italian', 'Portuguese', 'Japanese', 'Korean', 'Russian', 'Chinese'];

    teams.forEach((team, idx) => {
      const i = idx + 1;

      // Determine performance level based on index
      let performanceLevel: 'beginner' | 'intermediate' | 'advanced' | 'pro';
      let totalRaces: number;
      let rating: number;
      let totalWins: number;

      if (i % 8 === 0) {
        // Pro teams
        performanceLevel = 'pro';
        totalRaces = faker.number.int({ min: 80, max: 150 });
        rating = faker.number.int({ min: 1700, max: 2000 });
        totalWins = faker.number.int({ min: 15, max: 40 });
      } else if (i % 5 === 0) {
        // Advanced teams
        performanceLevel = 'advanced';
        totalRaces = faker.number.int({ min: 40, max: 100 });
        rating = faker.number.int({ min: 1500, max: 1800 });
        totalWins = faker.number.int({ min: 8, max: 20 });
      } else if (i % 3 === 0) {
        // Intermediate teams
        performanceLevel = 'intermediate';
        totalRaces = faker.number.int({ min: 20, max: 60 });
        rating = faker.number.int({ min: 1200, max: 1600 });
        totalWins = faker.number.int({ min: 3, max: 12 });
      } else {
        // Beginner teams
        performanceLevel = 'beginner';
        totalRaces = faker.number.int({ min: 5, max: 25 });
        rating = faker.number.int({ min: 900, max: 1300 });
        totalWins = faker.number.int({ min: 0, max: 5 });
      }

      // Determine specialization
      let specialization: 'endurance' | 'sprint' | 'mixed';
      if (i % 7 === 0) {
        specialization = 'endurance';
      } else if (i % 4 === 0) {
        specialization = 'sprint';
      } else {
        specialization = 'mixed';
      }

      // Determine category - use all available categories
      const categories = ['beginner', 'intermediate', 'advanced', 'pro', 'endurance', 'sprint'];
      const category = faker.helpers.arrayElement(categories);

      // Generate region and languages
      const region = faker.helpers.arrayElement(regions);
      const languageCount = faker.number.int({ min: 1, max: 3 });
      const languages = faker.helpers.arrayElements(allLanguages, languageCount);

      statsMap.set(team.id.toString(), {
        performanceLevel,
        specialization,
        region,
        languages,
        totalWins,
        totalRaces,
        rating,
        category,
      });
    });

    return statsMap;
  }

  private addDays(date: Date, days: number): Date {
    return new Date(date.getTime() + days * 24 * 60 * 60 * 1000);
  }
}