import { League } from '@core/racing/domain/entities/League';
import { Race } from '@core/racing/domain/entities/Race';
import { Result as RaceResult } from '@core/racing/domain/entities/result/Result';
import { Standing } from '@core/racing/domain/entities/Standing';
import { getPointsSystems } from '../PointsSystems';

export class RacingStandingFactory {
  create(leagues: League[], races: Race[], results: RaceResult[]): Standing[] {
    const pointsSystems = getPointsSystems();

    const racesByLeague = new Map<string, Set<string>>();
    for (const race of races) {
      if (!race.status.isCompleted()) continue;

      const set = racesByLeague.get(race.leagueId) ?? new Set<string>();
      set.add(race.id);
      racesByLeague.set(race.leagueId, set);
    }

    const standings: Standing[] = [];

    for (const league of leagues) {
      const leagueId = league.id.toString();
      const completedRaceIds = racesByLeague.get(leagueId) ?? new Set<string>();
      if (completedRaceIds.size === 0) continue;

      const pointsTable = this.resolvePointsSystem(league, pointsSystems);

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

      for (const result of results) {
        if (!completedRaceIds.has(result.raceId.toString())) continue;

        const driverId = result.driverId.toString();
        const previousStanding = byDriver.get(driverId) ?? Standing.create({ leagueId, driverId, position: 1 });
        const nextStanding = previousStanding.addRaceResult(result.position.toNumber(), pointsTable);
        byDriver.set(driverId, nextStanding);
      }

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

      sorted.forEach((standing, index) => standings.push(standing.updatePosition(index + 1)));
    }

    return standings;
  }

  private resolvePointsSystem(
    league: League,
    pointsSystems: Record<string, Record<number, number>>,
  ): Record<number, number> {
    const settings = league.settings;
    return settings.customPoints ?? pointsSystems[settings.pointsSystem] ?? pointsSystems['f1-2024'] ?? {};
  }
}