// Pure what-if bracket logic: user picks propagate winners through the
// knockout tree (placeholder codes 'W74'/'L101' reference earlier matches).
// Real finished results always win over picks.
import type { Fixture } from './types';

export type PickSide = 'home' | 'away';
export type Picks = Record<number, PickSide>;

export interface EffMatch {
  home: string | null;
  away: string | null;
  winner: string | null;
  loser: string | null;
  /** true when the winner comes from a real finished result (not pickable) */
  decided: boolean;
}

/** Resolve every knockout fixture's effective teams + winner under `picks`. */
export function effectiveBracket(fixtures: Fixture[], picks: Picks): Map<number, EffMatch> {
  const eff = new Map<number, EffMatch>();
  const slotTeam = (f: Fixture, side: PickSide): string | null => {
    const slot = side === 'home' ? f.home : f.away;
    if (slot.team) return slot.team;
    const m = /^([WL])(\d+)$/.exec(slot.placeholder ?? '');
    if (!m) return null;
    const src = eff.get(Number(m[2]));
    return src ? (m[1] === 'W' ? src.winner : src.loser) : null;
  };
  const ko = fixtures.filter((f) => f.stage !== 'group').sort((a, b) => a.num - b.num);
  for (const f of ko) {
    const home = slotTeam(f, 'home');
    const away = slotTeam(f, 'away');
    let winner: string | null = null;
    let loser: string | null = null;
    let decided = false;
    if (f.status === 'finished' && f.home.team && f.away.team && f.homeScore != null && f.awayScore != null && f.homeScore !== f.awayScore) {
      winner = f.homeScore > f.awayScore ? f.home.team : f.away.team;
      loser = f.homeScore > f.awayScore ? f.away.team : f.home.team;
      decided = true;
    } else {
      const pick = picks[f.num];
      if (pick && home && away) {
        winner = pick === 'home' ? home : away;
        loser = pick === 'home' ? away : home;
      }
    }
    eff.set(f.num, { home, away, winner, loser, decided });
  }
  return eff;
}

/** Drop picks that no longer make sense (an upstream pick changed, so the
 *  picked team is not in this match anymore). Call after every pick change. */
export function prunePicks(fixtures: Fixture[], picks: Picks): Picks {
  const out: Picks = {};
  let eff = effectiveBracket(fixtures, picks);
  // iterate to a fixed point: removing one stale pick can invalidate others
  for (let pass = 0; pass < 6; pass++) {
    let changed = false;
    for (const [numStr, side] of Object.entries(picks)) {
      const num = Number(numStr);
      const m = eff.get(num);
      if (m && !m.decided && m.home && m.away) {
        out[num] = side;
      } else if (out[num]) {
        delete out[num];
        changed = true;
      } else if (m && (m.decided || !m.home || !m.away)) {
        delete picks[num];
        changed = true;
      }
    }
    if (!changed) break;
    eff = effectiveBracket(fixtures, { ...out });
  }
  return out;
}

/** The picked champion's path: the matches they were picked to win, in order. */
export function championPath(
  fixtures: Fixture[],
  eff: Map<number, EffMatch>,
): { champion: string; matchNums: number[] } | null {
  const final = fixtures.find((f) => f.stage === 'final');
  if (!final) return null;
  const fm = eff.get(final.num);
  if (!fm?.winner) return null;
  const champion = fm.winner;
  const nums = [...eff.entries()]
    .filter(([num, m]) => m.winner === champion && !m.decided && fixtures.find((f) => f.num === num)?.stage !== 'third')
    .map(([num]) => num)
    .sort((a, b) => a - b);
  return { champion, matchNums: nums };
}
