// Group qualification scenarios: enumerate every outcome combination of the
// remaining group matches (W/D/L with representative one-goal margins) and
// rank each hypothetical table. Top-2 is decidable inside a group; third place
// runs through the cross-group best-thirds race and is NOT claimed here.
import { rankGroup, type Result } from './standings';
import type { Fixture } from './types';

export interface TeamScenario {
  team: string;
  /** outcome combinations in which the team finishes in the top 2 */
  top2Count: number;
  totalCombos: number;
  guaranteed: boolean;
  alive: boolean;
  /** top-2 in every combination where the team wins all its remaining matches
   *  (null when the team has nothing left to play) */
  safeWithWin: boolean | null;
}

const OUTCOMES: [number, number][] = [[1, 0], [0, 0], [0, 1]];

/** Scenarios for one group, or null when enumeration isn't meaningful yet
 *  (nothing left to play, or more than 3 group matches still open). */
export function groupScenarios(teams: string[], groupFixtures: Fixture[]): TeamScenario[] | null {
  const played: Result[] = [];
  const remaining: { home: string; away: string }[] = [];
  for (const f of groupFixtures) {
    if (!f.home.team || !f.away.team) return null; // group slots must be known
    if (f.status === 'finished' && f.homeScore != null && f.awayScore != null) {
      played.push({ home: f.home.team, away: f.away.team, homeScore: f.homeScore, awayScore: f.awayScore });
    } else {
      remaining.push({ home: f.home.team, away: f.away.team });
    }
  }
  if (remaining.length === 0 || remaining.length > 3) return null;

  const total = 3 ** remaining.length;
  const top2 = new Map<string, number>(teams.map((t) => [t, 0]));
  const winCombos = new Map<string, { total: number; top2: number }>(teams.map((t) => [t, { total: 0, top2: 0 }]));

  for (let combo = 0; combo < total; combo++) {
    const assumed: Result[] = [];
    const wonBy = new Set<string>();
    const lostOrDrew = new Set<string>();
    let c = combo;
    for (const m of remaining) {
      const [hs, as] = OUTCOMES[c % 3]!;
      c = Math.floor(c / 3);
      assumed.push({ home: m.home, away: m.away, homeScore: hs, awayScore: as });
      if (hs > as) { wonBy.add(m.home); lostOrDrew.add(m.away); }
      else if (as > hs) { wonBy.add(m.away); lostOrDrew.add(m.home); }
      else { lostOrDrew.add(m.home); lostOrDrew.add(m.away); }
    }
    const table = rankGroup(teams, [...played, ...assumed]);
    const topTwo = new Set(table.slice(0, 2).map((r) => r.team));
    for (const t of teams) {
      if (topTwo.has(t)) top2.set(t, top2.get(t)! + 1);
      // a combo counts toward "with a win" when the team won every remaining match it plays in
      const plays = remaining.some((m) => m.home === t || m.away === t);
      if (plays && wonBy.has(t) && !lostOrDrew.has(t)) {
        const w = winCombos.get(t)!;
        w.total++;
        if (topTwo.has(t)) w.top2++;
      }
    }
  }

  return teams.map((team) => {
    const n = top2.get(team)!;
    const w = winCombos.get(team)!;
    return {
      team,
      top2Count: n,
      totalCombos: total,
      guaranteed: n === total,
      alive: n > 0,
      safeWithWin: w.total === 0 ? null : w.top2 === w.total,
    };
  });
}
