/* ============================================================
   tournament.jsx — main menu + 9-max single-table tournament (SNG).
   Uses window.Table (engine + controller), window.Charts (multiway GTO
   grading), window.Engine (equity), and reuses window.UI feedback widgets.
   Exposes window.TournamentUI = { Menu, TournamentApp }.
   ============================================================ */
const { useState: useT, useRef: useR, useEffect: useE, useCallback: useC } = React;
const Tbl = window.Table, Ch = window.Charts, Eg = window.Engine, GT = window.GTO;
const { FeedbackOverlay: FBOverlay, Toast: TToast } = window.UI;
const Sfx = window.Sfx || { forAction() {}, deal() {}, win() {}, lose() {}, click() {}, isEnabled: () => false, setEnabled() {} };
const chips = (n) => Math.round(n).toLocaleString();

// 9 seat anchor points around the oval rim (% of table box). id 0 = hero, bottom-center.
const POS9 = [
  { x: 50, y: 93 }, { x: 18, y: 85 }, { x: 6, y: 57 }, { x: 11, y: 27 }, { x: 33, y: 10 },
  { x: 67, y: 10 }, { x: 89, y: 27 }, { x: 94, y: 57 }, { x: 82, y: 85 },
];
const TCENTER = { x: 50, y: 50 };
// point between a seat and the pot (for bet chips / dealer button)
const lerp = (p, t) => ({ x: TCENTER.x + (p.x - TCENTER.x) * t, y: TCENTER.y + (p.y - TCENTER.y) * t });

/* ---------- bot policy (multiway) ---------- */
function botMove(H, id) {
  const s = H.seats[id], bb = H.blinds.bb;
  const toCall = Math.max(0, H.currentBet - s.street);
  const legal = Tbl.legalActions(H).map((a) => a.type);
  const has = (t) => legal.includes(t);
  const sizeTo = (mult) => Math.min(Tbl.maxRaiseTo(H), Math.max(Tbl.minRaiseTo(H), Math.round(mult)));
  if (H.street === "preflop") {
    const code = GT.codeOfHole(s.hole);
    let behind = 0; for (const oid of H.ring) { const x = H.seats[oid]; if (oid !== id && !x.folded && !x.acted) behind++; }
    const raised = H.currentBet > bb + 1e-9;
    if (!raised && toCall === 0) return GT.pctlOf(s.hole) > 0.9 && has("raise") ? { type: "raise", to: sizeTo(2.3 * bb) } : { type: "check" };
    const facing = !raised ? "first-in" : (toCall >= s.stack * 0.6 ? "shove" : "raise");
    const adv = Ch.advise({ code, effStackBB: s.stack / bb, playersBehind: behind, facing, toCallBB: toCall / bb, potBB: Tbl.potTotal(H) / bb, posName: "" });
    if (adv.action === "shove" && has("raise")) return { type: "raise", to: s.street + s.stack };
    if (adv.action === "raise" && has("raise")) return { type: "raise", to: sizeTo(raised ? H.currentBet * 3 : 2.3 * bb) };
    if (adv.action === "call" && has("call")) return { type: "call", to: H.currentBet };
    if (has("check")) return { type: "check" };
    return { type: "fold" };
  }
  const numOpp = H.ring.filter((o) => !H.seats[o].folded).length - 1;
  const eq = Eg.equityMulti(s.hole, H.board, Math.max(1, numOpp), 200);
  const pot = Tbl.potTotal(H), potOdds = toCall > 0 ? toCall / (pot + toCall) : 0;
  if (toCall > 0) {
    if (eq > 0.66 && has("raise") && Math.random() < 0.5) return { type: "raise", to: sizeTo(H.currentBet + pot * 0.6) };
    if (eq >= potOdds + 0.03 && has("call")) return { type: "call", to: H.currentBet };
    return has("check") ? { type: "check" } : { type: "fold" };
  }
  if (eq > 0.55 && has("bet")) return { type: "bet", to: s.street + Math.round(pot * 0.6) };
  return { type: "check" };
}

/* ---------- main menu ---------- */
function Menu({ onPick }) {
  return (
    <div className="menu">
      <div className="menu-hero">
        <div className="menu-brand"><span className="dot" />GTO&nbsp;POKER</div>
        <div className="menu-sub">A No-Limit Hold'em trainer that grades every decision against a solver — with a detailed reason why.</div>
      </div>
      <div className="menu-grid">
        <button className="menu-card" onClick={() => onPick("hu")}>
          <div className="mc-ico">♠</div>
          <div className="mc-tag">Heads-Up · Live Solver</div>
          <div className="mc-title">1v1 Training</div>
          <div className="mc-desc">You versus one opponent. Every preflop spot is graded against a real-time CFR solve with mixed frequencies.</div>
          <ul className="mc-feat">
            <li>Live CFR Nash solver</li>
            <li>Full range-grid viewer</li>
            <li>Preset drills + GTO Wizard links</li>
          </ul>
          <div className="mc-go">Train heads-up →</div>
        </button>
        <button className="menu-card" onClick={() => onPick("tourney")}>
          <div className="mc-ico">♦</div>
          <div className="mc-tag">9-Max · Sit &amp; Go</div>
          <div className="mc-title">Tournament</div>
          <div className="mc-desc">A full 9-handed tournament: 1,500 chips, rising blinds, real side-pots — play down to one winner.</div>
          <ul className="mc-feat">
            <li>Nash push/fold + position ranges</li>
            <li>Real side-pots &amp; eliminations</li>
            <li>Graded feedback on every hand</li>
          </ul>
          <div className="mc-go">Play tournament →</div>
        </button>
      </div>
      <div className="menu-chips">
        <span>♥ 7-card evaluator</span><span>◆ Monte-Carlo equity</span><span>♣ Mixed-strategy GTO</span><span>★ Why on every play</span>
      </div>
      <div className="menu-foot">Heads-up preflop is a real live solve. Multiway &amp; postflop use honest published charts + equity heuristics — not a real-time solve.</div>
    </div>
  );
}

/* ---------- tournament table ---------- */
function TournamentApp({ onMenu }) {
  const [, setTick] = useT(0);
  const rerender = useC(() => setTick((t) => t + 1), []);
  const tourRef = useR(null);
  const handRef = useR(null);
  const botTimer = useR(null);
  const keyRef = useR(null);
  const sizeRef = useR(null); // current raise sizing exposed by the action bar (for the R key)
  const heroSeat = 0;
  const [busy, setBusy] = useT(false);
  const [waitNext, setWaitNext] = useT(false);
  const [pending, setPending] = useT(null);
  const [toast, setToast] = useT(null);
  const [study, setStudy] = useT(true);
  const [msg, setMsg] = useT("");
  const [muted, setMuted] = useT(!Sfx.isEnabled());
  const toggleSound = () => { const on = !Sfx.isEnabled(); Sfx.setEnabled(on); setMuted(!on); if (on) Sfx.click(); };

  useE(() => { newTourney(); return () => clearTimeout(botTimer.current); /* eslint-disable-next-line */ }, []);

  // keyboard shortcuts: F fold, C call/check, A all-in, N next/continue
  useE(() => {
    const onKey = (e) => {
      const t = e.target;
      if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
      if (e.metaKey || e.ctrlKey || e.altKey) return;
      const k = e.key === " " ? "n" : e.key.toLowerCase();
      if (["f", "c", "a", "r", "n", "enter"].includes(k)) { e.preventDefault(); keyRef.current && keyRef.current(k === "enter" ? "n" : k); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  function newTourney() {
    clearTimeout(botTimer.current);
    setWaitNext(false); setPending(null); setToast(null); setMsg("");
    tourRef.current = Tbl.createTournament({ players: 9, startStack: 1500, heroSeat });
    nextHand();
  }
  function nextHand() {
    const T = tourRef.current;
    setWaitNext(false); setPending(null); setToast(null); setMsg("");
    const H = Tbl.startHand(T);
    handRef.current = H;
    Sfx.deal();
    rerender();
    step();
  }
  function canHeroAct(H) { return H && !H.finished && H.toAct === heroSeat && !H.seats[heroSeat].folded && !H.seats[heroSeat].allIn && H.seats[heroSeat].stack > 0; }

  function step() {
    const H = handRef.current;
    if (!H) return;
    if (H.finished) { onHandEnd(); return; }
    if (canHeroAct(H)) { setBusy(false); rerender(); return; }
    setBusy(true); rerender();
    botTimer.current = setTimeout(() => {
      const h = handRef.current;
      if (!h || h.finished) { onHandEnd(); return; }
      if (canHeroAct(h)) { setBusy(false); rerender(); return; }
      const id = h.toAct;
      const mv = botMove(h, id);
      const allin = (mv.type === "raise" || mv.type === "bet") && mv.to >= h.seats[id].street + h.seats[id].stack - 0.5;
      Tbl.applyAction(h, mv);
      Sfx.forAction(mv.type, allin);
      rerender();
      step();
    }, 480 + Math.random() * 360);
  }

  function onHandEnd() {
    const T = tourRef.current, H = handRef.current;
    setBusy(false);
    const heroWon = H && H.result && H.result.pots && H.result.pots.some((p) => p.winners.includes(heroSeat));
    Tbl.finishHand(T);
    rerender();
    if (T.finished) { const champ = T.winner === heroSeat; champ ? Sfx.win() : Sfx.lose(); setMsg(champ ? "🏆 You win the tournament!" : `Tournament over — ${T.players[T.winner].name} wins. You placed ${T.players[heroSeat].place}.`); setWaitNext("done"); return; }
    if (T.players[heroSeat].eliminated) { Sfx.lose(); setMsg(`You busted — ${ordinal(T.players[heroSeat].place)} place of ${T.n}.`); setWaitNext("done"); return; }
    if (heroWon) Sfx.win();
    setWaitNext(true);
  }

  function heroAct(action) {
    const H = handRef.current;
    if (!canHeroAct(H)) return;
    const fb = Ch.grade(H, heroSeat, action);
    if (study) { setPending({ fb, action }); rerender(); }
    else { setToast(fb); setTimeout(() => setToast(null), 1600); applyHero(action); }
  }
  function applyHero(action) {
    setPending(null);
    const h = handRef.current, s = h.seats[heroSeat];
    const allin = (action.type === "raise" || action.type === "bet") && action.to >= s.street + s.stack - 0.5;
    Tbl.applyAction(h, action);
    Sfx.forAction(action.type, allin);
    rerender(); step();
  }

  const T = tourRef.current, H = handRef.current;
  if (!T) return null;
  const bl = Tbl.blinds(T);
  const heroH = H && H.seats[heroSeat];
  const reveal = H && H.finished && H.result && H.result.type === "showdown";
  const winnerIds = H && H.finished && H.result && H.result.pots ? new Set(H.result.pots.flatMap((p) => p.winners)) : new Set();

  keyRef.current = (k) => {
    if (pending) { if (k === "n" || k === "c") applyHero(pending.action); return; }
    if (waitNext) { if (k === "n" || k === "c") (waitNext === "done" ? newTourney() : nextHand()); return; }
    const h = handRef.current;
    if (!canHeroAct(h)) return;
    const legal = Tbl.legalActions(h).map((a) => a.type);
    const s = h.seats[heroSeat];
    if (k === "f") heroAct(legal.includes("fold") ? { type: "fold" } : { type: "check" });
    else if (k === "c") heroAct(legal.includes("call") ? { type: "call", to: h.currentBet } : { type: "check" });
    else if (k === "r" && (legal.includes("raise") || legal.includes("bet")) && sizeRef.current != null) heroAct({ type: legal.includes("raise") ? "raise" : "bet", to: sizeRef.current });
    else if (k === "a" && (legal.includes("raise") || legal.includes("bet"))) heroAct({ type: legal.includes("raise") ? "raise" : "bet", to: s.street + s.stack });
  };

  return (
    <div className="app">
      <header className="header theader">
        <div className="brand"><div className="name"><span className="dot" />GTO POKER · <span style={{ color: "var(--text-2)", fontWeight: 600 }}>Tournament</span></div>
          <div className="sub">9-max sit & go</div></div>
        <div className="seg" role="tablist">
          <button className={study ? "on" : ""} onClick={() => setStudy(true)}>Study</button>
          <button className={!study ? "on" : ""} onClick={() => setStudy(false)}>Play</button>
        </div>
        <div className="hspace" />
        <div className="stat-pill"><span className="v mono">{bl.sb}/{bl.bb}</span><span className="l">blinds · L{T.level + 1}</span></div>
        <div className="stat-pill"><span className="v mono">{Tbl.aliveSeats(T).length}</span><span className="l">left</span></div>
        <div className="stat-pill"><span className="v mono">{chips(T.players[heroSeat].stack)}</span><span className="l">your stack</span></div>
        <button className="iconbtn" onClick={toggleSound} title={muted ? "Unmute" : "Mute"}>{muted ? "🔇" : "🔊"}</button>
        <button className="iconbtn" onClick={onMenu} title="Main menu">☰</button>
      </header>

      <div className="tablewrap">
        <div className="tarena">
        <div className="ttable">
          {H && <div className="tpot mono">POT {chips(Tbl.potTotal(H) - (H.finished ? potJustWon(H) : 0))}</div>}
          {H && <div className="tboard">{H.board.map((c, i) => <window.UI.Card key={i} c={c} />)}{Array.from({ length: 5 - H.board.length }).map((_, i) => <div key={"e" + i} className="board-slot" />)}</div>}
          {H && msg && <div className="tmsg">{msg}</div>}
          {H && (() => { const b = lerp(POS9[H.btnId], 0.34); return <div className="tbtn" style={{ left: b.x + "%", top: b.y + "%" }} title="Dealer">D</div>; })()}
          {T.players.map((p) => {
            const inHand = H && H.seats[p.id] && !p.eliminated;
            const s = inHand ? H.seats[p.id] : null;
            const pos = POS9[p.id];
            const role = H ? (p.id === H.sbId ? "SB" : p.id === H.bbId ? "BB" : "") : "";
            return (
              <div key={p.id} className={"tseat" + (p.id === heroSeat ? " hero" : "") + (s && H.toAct === p.id && !H.finished ? " active" : "") + (p.eliminated ? " out" : "") + (s && s.folded ? " folded" : "")} style={{ left: pos.x + "%", top: pos.y + "%" }}>
                <div className="tseat-in">
                  <div className="nm">{p.name}{role && <span className="role">{role}</span>}</div>
                  <div className="stk mono">{p.eliminated ? "OUT" : chips(p.stack)}</div>
                  {s && !p.eliminated && !s.folded && <div className="thole">{(p.id === heroSeat || reveal || winnerIds.has(p.id)) ? s.hole.map((c, i) => <window.UI.Card key={i} c={c} tiny />) : [0, 1].map((i) => <window.UI.Card key={i} back tiny />)}</div>}
                  {s && s.allIn && !s.folded && <div className="allin">ALL-IN</div>}
                </div>
              </div>
            );
          })}
          {H && T.players.map((p) => {
            const s = H.seats[p.id];
            if (!s || p.eliminated || s.street <= 0) return null;
            const bp = lerp(POS9[p.id], 0.45);
            return <div key={"bet" + p.id} className="tbet mono" style={{ left: bp.x + "%", top: bp.y + "%" }}>{chips(s.street)}</div>;
          })}
        </div>
        </div>

        {/* action area */}
        {waitNext === "done" ? (
          <div className="actionbar"><div className="waiting">
            <button className="next-btn" onClick={newTourney}>New tournament →</button>
            <button className="ghost-btn" onClick={onMenu}>Main menu</button>
          </div></div>
        ) : waitNext ? (
          <div className="actionbar"><div className="waiting">
            <span className="mono" style={{ color: "var(--text-3)" }}>{handResultText(H, heroSeat)}</span>
            <button className="next-btn" onClick={nextHand}>Next hand →</button>
          </div></div>
        ) : (
          <TActionBar H={H} heroSeat={heroSeat} canAct={canHeroAct(H)} busy={busy} onAction={heroAct} sizeRef={sizeRef} />
        )}
      </div>

      {pending && <FBOverlay fb={pending.fb} onContinue={() => applyHero(pending.action)} />}
      {toast && <TToast fb={toast} />}
    </div>
  );
}

function potJustWon(H) { if (!H.result || !H.result.pots) return 0; return H.result.pots.reduce((a, p) => a + p.amount, 0); }
function ordinal(n) { const s = ["th", "st", "nd", "rd"], v = n % 100; return n + (s[(v - 20) % 10] || s[v] || s[0]); }
function handResultText(H, hero) {
  if (!H || !H.result || !H.result.pots || !H.result.pots.length) return "Hand complete";
  const nm = (id) => (id === hero ? "You" : (H.seats[id] && H.seats[id].name) || "Player");
  const w = H.result.pots[0].winners;
  const names = w.map(nm).join(" & ");
  const v = (w.length === 1 && w[0] === hero) ? "win" : "wins";
  if (w.length > 1) return `Split pot — ${names}.`;
  if (H.result.type === "fold") return `${names} ${v} — everyone folded.`;
  const hand = H.result.hands && H.result.hands[w[0]];
  return `${names} ${v}${hand ? " with " + hand : ""}.`;
}

/* ---------- tournament action bar (chips) ---------- */
function TActionBar({ H, heroSeat, canAct, busy, onAction, sizeRef }) {
  const s = H && H.seats[heroSeat];
  const toCall = s ? Math.max(0, H.currentBet - s.street) : 0;
  const pot = H ? Tbl.potTotal(H) : 0;
  const minTo = H ? Tbl.minRaiseTo(H) : 0, maxTo = s ? s.street + s.stack : 0;
  const canAggro = s && s.stack > toCall;
  const [to, setTo] = useT(minTo);
  useE(() => {
    let def = H && H.street === "preflop" ? Math.min(maxTo, Math.max(minTo, Math.round((H.currentBet > H.blinds.bb ? H.currentBet * 3 : H.blinds.bb * 2.3))))
      : Math.min(maxTo, Math.max(minTo, Math.round((s ? s.street : 0) + pot * 0.6)));
    setTo(def || minTo);
    // eslint-disable-next-line
  }, [H && H.toAct, H && H.street, H && H.history.length]);
  // publish current raise sizing for the R shortcut
  if (sizeRef) sizeRef.current = (H && !H.finished && canAct && canAggro) ? to : null;

  if (!H || H.finished || !canAct) {
    if (sizeRef) sizeRef.current = null;
    const actor = H && !H.finished && H.toAct != null && H.seats[H.toAct] ? H.seats[H.toAct].name : null;
    return <div className="actionbar"><div className="waiting"><span className="mono" style={{ color: "var(--text-3)" }}>{actor ? `${actor} is acting…` : "Waiting…"}</span></div></div>;
  }
  const raiseVerb = toCall > 0 ? "Raise" : "Bet";
  const raiseType = toCall > 0 ? "raise" : "bet";
  const presets = [
    ["½", { frac: 0.5 }], ["¾", { frac: 0.75 }], ["Pot", { frac: 1 }],
    ["2bb", { bb: 2 }], ["3bb", { bb: 3 }], ["4bb", { bb: 4 }], ["5bb", { bb: 5 }],
    ["All-in", { allin: true }],
  ];
  const applyPreset = (p) => {
    const d = p[1]; let v;
    if (d.allin) v = maxTo;
    else if (d.bb != null) v = d.bb * H.blinds.bb;            // total bet of N big blinds
    else v = s.street + d.frac * (pot + toCall);              // pot-fraction raise/bet
    setTo(Math.max(minTo, Math.min(maxTo, Math.round(v))));
  };
  return (
    <div className="actionbar">
      {canAggro && (
        <div className="sizing">
          {presets.map((p) => <button key={p[0]} className="sizechip" onClick={() => applyPreset(p)}>{p[0]}</button>)}
          <div className="slider-wrap">
            <input type="range" min={minTo} max={maxTo} step={H.blinds.sb} value={to} onChange={(e) => setTo(parseInt(e.target.value))} />
            <span className="raise-amt mono">{chips(to)}</span>
          </div>
        </div>
      )}
      <div className="act-row">
        {toCall > 0 ? <button className="btn fold" onClick={() => onAction({ type: "fold" })}>Fold</button>
          : <button className="btn fold" onClick={() => onAction({ type: "check" })}>Check</button>}
        {toCall > 0 && <button className="btn call" onClick={() => onAction({ type: "call", to: H.currentBet })}>Call<span className="sub">{chips(Math.min(toCall, s.stack))}</span></button>}
        {canAggro && <button className="btn raise" onClick={() => onAction({ type: raiseType, to })}>{raiseVerb}<span className="sub">to {chips(to)}</span></button>}
      </div>
      <div className="keyhint"><b>F</b> {toCall > 0 ? "fold" : "check"} · <b>C</b> {toCall > 0 ? "call" : "check"}{canAggro && <>{" · "}<b>R</b> raise{" · "}<b>A</b> all-in</>} · <b>N</b> next</div>
    </div>
  );
}

window.TournamentUI = { Menu, TournamentApp };
