import type { Fixture, StandingRow } from './types';

interface Tally {
  team: string;
  played: number;
  won: number;
  drawn: number;
  lost: number;
  gf: number;
  ga: number;
  points: number;
}

function blank(team: string): Tally {
  return { team, played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, points: 0 };
}

/** A single finished result between two known teams. */
export interface Result {
  home: string;
  away: string;
  homeScore: number;
  awayScore: number;
}

function applyResult(tallies: Map<string, Tally>, r: Result): void {
  const h = tallies.get(r.home);
  const a = tallies.get(r.away);
  if (!h || !a) return;
  h.played++; a.played++;
  h.gf += r.homeScore; h.ga += r.awayScore;
  a.gf += r.awayScore; a.ga += r.homeScore;
  if (r.homeScore > r.awayScore) { h.won++; a.lost++; h.points += 3; }
  else if (r.homeScore < r.awayScore) { a.won++; h.lost++; a.points += 3; }
  else { h.drawn++; a.drawn++; h.points++; a.points++; }
}

/** Head-to-head sub-ranking among a set of teams that are level on pts/gd/gf. */
function headToHead(tied: string[], results: Result[]): Map<string, number> {
  const sub = new Map(tied.map((t) => [t, blank(t)]));
  for (const r of results) {
    if (sub.has(r.home) && sub.has(r.away)) applyResult(sub, r);
  }
  // Rank within the mini-table by pts, gd, gf.
  const ranked = [...sub.values()].sort((x, y) =>
    y.points - x.points || (y.gf - y.ga) - (x.gf - x.ga) || y.gf - x.gf,
  );
  return new Map(ranked.map((t, i) => [t.team, i]));
}

/**
 * Rank a group's teams by the FIFA criteria: points, goal difference, goals
 * for, then head-to-head (points/GD/GF among the tied), then name as a stable
 * final fallback (the real tournament uses fair-play points / drawing of lots).
 */
export function rankGroup(teams: string[], results: Result[]): StandingRow[] {
  const tallies = new Map(teams.map((t) => [t, blank(t)]));
  for (const r of results) applyResult(tallies, r);

  const rows = [...tallies.values()];
  rows.sort((x, y) => {
    const byPts = y.points - x.points;
    if (byPts) return byPts;
    const byGd = (y.gf - y.ga) - (x.gf - x.ga);
    if (byGd) return byGd;
    const byGf = y.gf - x.gf;
    if (byGf) return byGf;
    return 0; // resolve ties below via head-to-head
  });

  // Resolve any cluster level on pts+gd+gf with a head-to-head mini-table.
  const out: Tally[] = [];
  let i = 0;
  while (i < rows.length) {
    let j = i + 1;
    const a = rows[i]!;
    while (
      j < rows.length &&
      rows[j]!.points === a.points &&
      rows[j]!.gf - rows[j]!.ga === a.gf - a.ga &&
      rows[j]!.gf === a.gf
    ) j++;
    const cluster = rows.slice(i, j);
    if (cluster.length > 1) {
      const order = headToHead(cluster.map((t) => t.team), results);
      cluster.sort(
        (x, y) => (order.get(x.team) ?? 0) - (order.get(y.team) ?? 0) || x.team.localeCompare(y.team),
      );
    }
    out.push(...cluster);
    i = j;
  }

  return out.map((t, idx) => ({
    team: t.team,
    played: t.played,
    won: t.won,
    drawn: t.drawn,
    lost: t.lost,
    gf: t.gf,
    ga: t.ga,
    gd: t.gf - t.ga,
    points: t.points,
    rank: idx + 1,
  }));
}

/** Build ranked standings for every group from the current fixtures. */
export function computeGroupTables(fixtures: Fixture[]): Record<string, StandingRow[]> {
  const groups = new Map<string, Set<string>>();
  const results = new Map<string, Result[]>();

  for (const f of fixtures) {
    if (f.stage !== 'group' || !f.group) continue;
    const g = f.group;
    if (!groups.has(g)) { groups.set(g, new Set()); results.set(g, []); }
    if (f.home.team) groups.get(g)!.add(f.home.team);
    if (f.away.team) groups.get(g)!.add(f.away.team);
    if (
      f.status === 'finished' &&
      f.home.team && f.away.team &&
      f.homeScore !== null && f.awayScore !== null
    ) {
      results.get(g)!.push({
        home: f.home.team, away: f.away.team,
        homeScore: f.homeScore, awayScore: f.awayScore,
      });
    }
  }

  const tables: Record<string, StandingRow[]> = {};
  for (const [g, teamSet] of [...groups.entries()].sort(([a], [b]) => a.localeCompare(b))) {
    tables[g] = rankGroup([...teamSet].sort(), results.get(g) ?? []);
  }
  return tables;
}
