import { Driver } from '@core/racing/domain/entities/Driver';
import { League } from '@core/racing/domain/entities/League';
import { Race } from '@core/racing/domain/entities/Race';
import { Result } from '@core/racing/domain/entities/Result';
import { Standing } from '@core/racing/domain/entities/Standing';
import { SessionType } from '@core/racing/domain/value-objects/SessionType';
import { faker } from '../../helpers/faker/faker';

/**
 * Core racing seed types and generators (drivers, leagues, teams, races, standings).
 * Extracted from the legacy StaticRacingSeed module to keep files smaller and focused.
 */
export type RacingMembership = {
  driverId: string;
  leagueId: string;
  teamId?: string;
};

export type Friendship = {
  driverId: string;
  friendId: string;
};

export interface DemoTeamDTO {
  id: string;
  name: string;
  tag: string;
  description: string;
  logoUrl: string;
  primaryLeagueId: string;
  memberCount: number;
}

/**
 * Championship points table used when aggregating standings.
 */
export const POINTS_TABLE: Record<number, number> = {
  1: 25,
  2: 18,
  3: 15,
  4: 12,
  5: 10,
  6: 8,
  7: 6,
  8: 4,
  9: 2,
  10: 1,
};

export function pickOne<T>(items: readonly T[]): T {
  if (items.length === 0) {
    throw new Error('pickOne: empty items array');
  }
  const index = faker.number.int({ min: 0, max: items.length - 1 });
  return items[index]!;
}

export function createDrivers(count: number): Driver[] {
  const drivers: Driver[] = [];

  for (let i = 0; i < count; i++) {
    const id = `driver-${i + 1}`;
    const name = faker.person.fullName();
    const country = faker.location.countryCode('alpha-2');
    const iracingId = faker.string.numeric(6);

    drivers.push(
      Driver.create({
        id,
        iracingId,
        name,
        country,
        bio: faker.lorem.sentence(),
        joinedAt: faker.date.past(),
      }),
    );
  }

  return drivers;
}

export function createLeagues(ownerIds: string[]): League[] {
  const leagueNames = [
    'GridPilot Sprint Series',
    'GridPilot Endurance Cup',
    'GridPilot Club Ladder',
    'Sprint Challenge League',
    'Club Racers Collective',
    'Sim Racing Alliance',
    'Pacific Time Attack',
    'Nordic Night Series',
  ];

  const leagues: League[] = [];
  const leagueCount = 6 + faker.number.int({ min: 0, max: 2 });

  for (let i = 0; i < leagueCount; i++) {
    const id = `league-${i + 1}`;
    const name = leagueNames[i] ?? faker.company.name();
    // Ensure league-5 (demo league with running race) is owned by driver-1
    const ownerId = i === 4 ? 'driver-1' : pickOne(ownerIds);

    const maxDriversOptions = [24, 32, 48, 64];
    let settings = {
      pointsSystem: faker.helpers.arrayElement(['f1-2024', 'indycar']),
      sessionDuration: faker.helpers.arrayElement([45, 60, 90, 120]),
      qualifyingFormat: faker.helpers.arrayElement(['open', 'single-lap']),
      maxDrivers: faker.helpers.arrayElement(maxDriversOptions),
    } as const;

    if (i === 0) {
      settings = {
        ...settings,
        maxDrivers: 24,
      };
    } else if (i === 1) {
      settings = {
        ...settings,
        maxDrivers: 24,
      };
    } else if (i === 2) {
      settings = {
        ...settings,
        maxDrivers: 40,
      };
    }

    const socialLinks =
      i === 0
        ? {
            discordUrl: 'https://discord.gg/gridpilot-demo',
            youtubeUrl: 'https://youtube.com/@gridpilot-demo',
            websiteUrl: 'https://gridpilot-demo.example.com',
          }
        : i === 1
        ? {
            discordUrl: 'https://discord.gg/gridpilot-endurance',
            youtubeUrl: 'https://youtube.com/@gridpilot-endurance',
          }
        : i === 2
        ? {
            websiteUrl: 'https://virtual-touring.example.com',
          }
        : undefined;
 
    if (socialLinks) {
      leagues.push(
        League.create({
          id,
          name,
          description: faker.lorem.sentence(),
          ownerId,
          settings,
          createdAt: faker.date.past(),
          socialLinks,
        }),
      );
    } else {
      leagues.push(
        League.create({
          id,
          name,
          description: faker.lorem.sentence(),
          ownerId,
          settings,
          createdAt: faker.date.past(),
        }),
      );
    }
  }

  return leagues;
}

export function createTeams(leagues: League[], getTeamLogo: (id: string) => string): DemoTeamDTO[] {
  const teams: DemoTeamDTO[] = [];
  const teamCount = 24 + faker.number.int({ min: 0, max: 12 });

  for (let i = 0; i < teamCount; i++) {
    const id = `team-${i + 1}`;
    const primaryLeague = pickOne(leagues);
    const name = faker.company.name();
    const tag = faker.string.alpha({ length: 4 }).toUpperCase();
    const memberCount = faker.number.int({ min: 2, max: 8 });

    teams.push({
      id,
      name,
      tag,
      description: faker.lorem.sentence(),
      logoUrl: getTeamLogo(id),
      primaryLeagueId: primaryLeague.id,
      memberCount,
    });
  }

  return teams;
}

export function createMemberships(
  drivers: Driver[],
  leagues: League[],
  teams: DemoTeamDTO[],
): RacingMembership[] {
  const memberships: RacingMembership[] = [];

  const teamsByLeague = new Map<string, DemoTeamDTO[]>();
  teams.forEach((team) => {
    const list = teamsByLeague.get(team.primaryLeagueId) ?? [];
    list.push(team);
    teamsByLeague.set(team.primaryLeagueId, list);
  });


  drivers.forEach((driver) => {
    // Each driver participates in 1–3 leagues
    const leagueSampleSize = faker.number.int({ min: 1, max: Math.min(3, leagues.length) });
    const shuffledLeagues = faker.helpers.shuffle(leagues).slice(0, leagueSampleSize);

    shuffledLeagues.forEach((league) => {
      const leagueTeams = teamsByLeague.get(league.id) ?? [];
      const team =
        leagueTeams.length > 0 && faker.datatype.boolean()
          ? pickOne(leagueTeams)
          : undefined;

      const membership: RacingMembership = {
        driverId: driver.id,
        leagueId: league.id,
      };

      if (team) {
        membership.teamId = team.id;
      }

      memberships.push(membership);
    });
  });

  return memberships;
}

export function createRaces(leagues: League[]): Race[] {
  const races: Race[] = [];
  const raceCount = 60 + faker.number.int({ min: 0, max: 20 });

  const tracks = [
    'Monza GP',
    'Spa-Francorchamps',
    'Suzuka',
    'Mount Panorama',
    'Silverstone GP',
    'Interlagos',
    'Imola',
    'Laguna Seca',
  ];

  const cars = [
    'GT3 – Porsche 911',
    'GT3 – BMW M4',
    'LMP3 Prototype',
    'GT4 – Alpine',
    'Touring – Civic',
  ];

  const baseDate = new Date();

  for (let i = 0; i < raceCount; i++) {
    const id = `race-${i + 1}`;
    let league = pickOne(leagues);
    const offsetDays = faker.number.int({ min: -30, max: 45 });
    const scheduledAt = new Date(baseDate.getTime() + offsetDays * 24 * 60 * 60 * 1000);
    let status: 'scheduled' | 'completed' | 'running' = scheduledAt.getTime() < baseDate.getTime() ? 'completed' : 'scheduled';
    let strengthOfField: number | undefined;

    // Special case: Make race-1 a running race in league-5 (user's admin league)
    if (i === 0) {
      const league5 = leagues.find(l => l.id === 'league-5');
      if (league5) {
        league = league5;
        status = 'running';
        // Calculate SOF for the running race (simulate 12-20 drivers with average rating ~1500)
        const participantCount = faker.number.int({ min: 12, max: 20 });
        const averageRating = 1500 + faker.number.int({ min: -200, max: 300 });
        strengthOfField = Math.round(averageRating);
      }
    }

    races.push(
      Race.create({
        id,
        leagueId: league.id,
        scheduledAt,
        track: faker.helpers.arrayElement(tracks),
        car: faker.helpers.arrayElement(cars),
        sessionType: SessionType.main(),
        status,
        ...(strengthOfField !== undefined ? { strengthOfField } : {}),
        ...(status === 'running' ? { registeredCount: faker.number.int({ min: 12, max: 20 }) } : {}),
      }),
    );
  }

  return races;
}

export function createResults(drivers: Driver[], races: Race[]): Result[] {
  const results: Result[] = [];

  const completedRaces = races.filter((race) => race.status === 'completed');

  completedRaces.forEach((race) => {
    const participantCount = faker.number.int({ min: 20, max: 32 });
    const shuffledDrivers = faker.helpers.shuffle(drivers).slice(0, participantCount);

    shuffledDrivers.forEach((driver, index) => {
      const position = index + 1;
      const startPosition = faker.number.int({ min: 1, max: participantCount });
      const fastestLap = 90_000 + index * 250 + faker.number.int({ min: 0, max: 2_000 });
      const incidents = faker.number.int({ min: 0, max: 6 });

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

  return results;
}

export function createStandings(leagues: League[], results: Result[]): Standing[] {
  const standingsByLeague = new Map<string, Standing[]>();

  leagues.forEach((league) => {
    const leagueRaceIds = new Set(
      results
        .filter((result) => {
          return result.raceId.startsWith('race-');
        })
        .map((result) => result.raceId),
    );

    const leagueResults = results.filter((result) => leagueRaceIds.has(result.raceId));

    const standingsMap = new Map<string, Standing>();

    leagueResults.forEach((result) => {
      const key = result.driverId;
      let standing = standingsMap.get(key);

      if (!standing) {
        standing = Standing.create({
          leagueId: league.id,
          driverId: result.driverId,
        });
      }

      standing = standing.addRaceResult(result.position, POINTS_TABLE);
      standingsMap.set(key, standing);
    });

    const sortedStandings = Array.from(standingsMap.values()).sort((a, b) => {
      if (b.points !== a.points) {
        return b.points - a.points;
      }
      if (b.wins !== a.wins) {
        return b.wins - a.wins;
      }
      return b.racesCompleted - a.racesCompleted;
    });

    const finalizedStandings = sortedStandings.map((standing, index) =>
      standing.updatePosition(index + 1),
    );

    standingsByLeague.set(league.id, finalizedStandings);
  });

  return Array.from(standingsByLeague.values()).flat();
}

export function createFriendships(drivers: Driver[]): Friendship[] {
  const friendships: Friendship[] = [];

  drivers.forEach((driver, index) => {
    const friendCount = faker.number.int({ min: 3, max: 8 });
    for (let offset = 1; offset <= friendCount; offset++) {
      const friendIndex = (index + offset) % drivers.length;
      const friend = drivers[friendIndex];
      if (!friend) continue;
      if (friend.id === driver.id) continue;

      friendships.push({
        driverId: driver.id,
        friendId: friend.id,
      });
    }
  });

  return friendships;
}