/* NBA PREDICTOR — main app. */

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "hca": 2.7,
  "injuries": true,
  "statsBlend": 50,
  "theme": "newsprint"
}/*EDITMODE-END*/;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [league, setLeague] = useState(null);
  const [homeId, setHomeId] = useState("SAS");
  const [awayId, setAwayId] = useState("NYK");
  // User-set starting lineups, keyed by team id → { POS: playerName }.
  const [lineups, setLineups] = useState({});
  // Prediction tracking / calibration.
  const [record, setRecord] = useState(null);
  const [tracked, setTracked] = useState({});
  const [backfilling, setBackfilling] = useState(false);

  useEffect(() => {
    window.NBAData.loadLeague().then(setLeague);
    fetch("/api/record").then((r) => r.ok && r.json()).then((d) => d && setRecord(d)).catch(() => {});
  }, []);

  // Apply theme via data attribute (theme palettes live in static CSS).
  useEffect(() => {
    document.documentElement.dataset.theme = t.theme;
  }, [t.theme]);

  const result = useMemo(() => {
    if (!league) return null;
    const home = league.teams.find((x) => x.id === homeId);
    const away = league.teams.find((x) => x.id === awayId);
    if (!home || !away) return null;
    return window.NBAModel.predict(home, away, {
      hca: t.hca,
      statsWeight: t.statsBlend / 100,
      applyInjuries: t.injuries,
      starterOverrides: { home: lineups[homeId], away: lineups[awayId] },
    });
  }, [league, homeId, awayId, t.hca, t.statsBlend, t.injuries, lineups]);

  if (!league) return <div className="loading">Loading league data…</div>;

  const teams = [...league.teams].sort((a, b) => (a.city + a.name).localeCompare(b.city + b.name));

  function setHome(id) { if (id === awayId) setAwayId(homeId); setHomeId(id); }
  function setAway(id) { if (id === homeId) setHomeId(awayId); setAwayId(id); }
  function swap() { const h = homeId; setHomeId(awayId); setAwayId(h); }

  // Hand-pick a starter at a position (name=null reverts that slot to auto).
  function setStarter(side, pos, name) {
    const tid = side === "home" ? homeId : awayId;
    setLineups((prev) => {
      const cur = { ...(prev[tid] || {}) };
      if (name == null) delete cur[pos]; else cur[pos] = name;
      return { ...prev, [tid]: cur };
    });
  }
  const edited = (lineups[homeId] && Object.keys(lineups[homeId]).length) || (lineups[awayId] && Object.keys(lineups[awayId]).length);
  function resetLineups() {
    setLineups((prev) => ({ ...prev, [homeId]: {}, [awayId]: {} }));
  }

  // Save the current prediction to the desk record (grades later vs. final score).
  const trackKey = awayId + "@" + homeId;
  function trackPrediction() {
    if (!result || tracked[trackKey]) return;
    const payload = {
      away: result.away.abbr, home: result.home.abbr,
      awayName: result.away.city + " " + result.away.name,
      homeName: result.home.city + " " + result.home.name,
      pick: result.fav.abbr, pPick: result.favProb, pHome: result.pHome, margin: result.margin,
      projScore: result.score, opts: { hca: t.hca, statsBlend: t.statsBlend, injuries: t.injuries },
    };
    setTracked((prev) => ({ ...prev, [trackKey]: true }));
    fetch("/api/predictions", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) })
      .then((r) => r.json()).then((d) => d.record && setRecord(d.record))
      .catch(() => setTracked((prev) => ({ ...prev, [trackKey]: false })));
  }

  // Backfill: run the model over recent real finals (ESPN) to populate calibration.
  function backfill() {
    if (backfilling) return;
    setBackfilling(true);
    const byId = Object.fromEntries(league.teams.map((tm) => [tm.id, tm]));
    fetch("/api/results?days=45").then((r) => r.json()).then((data) => {
      const items = [];
      for (const g of data.games || []) {
        const home = byId[g.home], away = byId[g.away];
        if (!home || !away) continue; // only matchups our data layer knows
        const pr = window.NBAModel.predict(home, away, { hca: t.hca, statsWeight: t.statsBlend / 100, applyInjuries: t.injuries });
        items.push({
          away: g.away, home: g.home,
          awayName: away.city + " " + away.name, homeName: home.city + " " + home.name,
          pick: pr.fav.abbr, pPick: pr.favProb, pHome: pr.pHome, margin: pr.margin, projScore: pr.score,
          opts: { hca: t.hca, statsBlend: t.statsBlend, injuries: t.injuries }, gameDate: g.date,
          status: g.winner === pr.fav.abbr ? "correct" : "incorrect",
          result: { homeScore: g.homeScore, awayScore: g.awayScore, winner: g.winner, source: "espn-backfill" },
        });
      }
      if (!items.length) { setBackfilling(false); return; }
      return fetch("/api/predictions/batch", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ items }) })
        .then((r) => r.json()).then((d) => d.record && setRecord(d.record));
    }).catch(() => {}).finally(() => setBackfilling(false));
  }

  const series = league.series && (league.series[awayId + "|" + homeId] || league.series[homeId + "|" + awayId]);

  return (
    <div className="wrap">
      <Masthead asOf={league.asOf} live={league.live} />
      <MatchupControls teams={teams} homeId={homeId} awayId={awayId} setHome={setHome} setAway={setAway} onSwap={swap} />
      <SeriesRibbon series={series} />

      {result && (
        <main className="report">
          <Verdict r={result} />
          <WinBar r={result} />
          <TrackBar tracked={!!tracked[trackKey]} onTrack={trackPrediction} record={record} />
          <SimPanel r={result} />
          <div className="grid-2">
            <LensBreakdown r={result} />
            <CategoryEdges r={result} />
          </div>
          {edited && (
            <div className="lineup-banner">
              <span>Custom lineups in effect — the projection reflects your edits.</span>
              <button onClick={resetLineups}>↺ Reset to auto</button>
            </div>
          )}
          <MatchupCards r={result} onSetStarter={setStarter} />
          <BenchPanel r={result} onSetStarter={setStarter} />
          <XFactors r={result} />
          <TrackRecord record={record} onBackfill={backfill} busy={backfilling} />
          <footer className="report-foot">
            <span>Model: net-rating + positional impact (BPM·PER·TS·USG) + context · win-prob via normal margin model (σ={"13.2"}).</span>
            <span>{league.live
              ? <>Live data via <code>stats.nba.com</code> proxy (PER &amp; BPM derived from PIE). Source: <code>/api/league</code>.</>
              : <>Illustrative 2025–26 data — start the proxy (<code>node server.js</code>) for live stats.</>}</span>
          </footer>
        </main>
      )}

      <TweaksPanel>
        <TweakSection label="Model" />
        <TweakSlider label="Home court advantage" value={t.hca} min={0} max={6} step={0.1} unit=" pts"
          onChange={(v) => setTweak("hca", v)} />
        <TweakSlider label="Blend · team stats vs. matchups" value={t.statsBlend} min={0} max={100} step={5} unit="% stats"
          onChange={(v) => setTweak("statsBlend", v)} />
        <TweakToggle label="Injuries &amp; rest" value={t.injuries}
          onChange={(v) => setTweak("injuries", v)} />
        <TweakSection label="Appearance" />
        <TweakRadio label="Theme" value={t.theme} options={["newsprint", "carbon", "slate"]}
          onChange={(v) => setTweak("theme", v)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
