/* App shell — simpler layout: left list of worst openings, right interactive board
   with prev/next navigation + Stockfish eval. */

const { useState, useEffect, useRef } = React;

function App() {
  const [me, setMe] = useState(undefined); // undefined=loading, null=anon
  const [data, setData] = useState(null);
  const [dataError, setDataError] = useState(null);
  const [filters, setFilters] = useState({
    color: 'white',
    tc: ['blitz', 'rapid'],
    rated: true,
    casual: false,
    oppMin: 1000,
    oppMax: 3000,
    date: '6mo',
    minGames: 3,
  });
  const [activeFen, setActiveFen] = useState(null);
  const [boardFen, setBoardFen] = useState(null); // can diverge from activeFen when user plays moves
  const [lastMove, setLastMove] = useState(null);
  const [sort, setSort] = useState('loss');
  const [sync, setSync] = useState({ state: 'idle', progress: 0, total: null, evalsDone: 0, evalsTotal: 0 });
  const [boardEval, setBoardEval] = useState(null); // { cp, depth, source }
  const [vendorReady, setVendorReady] = useState(!!window.__vendorReady);
  const [showFilters, setShowFilters] = useState(false);

  // Wait for chessground module to load
  useEffect(() => {
    if (vendorReady) return;
    const h = () => setVendorReady(true);
    window.addEventListener('vendor-ready', h);
    return () => window.removeEventListener('vendor-ready', h);
  }, [vendorReady]);

  useEffect(() => {
    fetchMe().then((m) => setMe(m)).catch(() => setMe(null));
  }, []);

  useEffect(() => {
    if (!me) return;
    setDataError(null);
    fetchTree(filters)
      .then((d) => {
        setData(d);
        setActiveFen((cur) => {
          if (cur && d.nodes[cur]) return cur;
          return d.worst?.[0]?.fen || d.rootFen || null;
        });
      })
      .catch((err) => setDataError(err.message || 'failed to load'));
  }, [me, filters]);

  // Keep boardFen in sync with activeFen when the active position changes via
  // the worst-list or prev/next buttons
  useEffect(() => {
    if (activeFen) {
      setBoardFen(activeFen);
      setLastMove(null);
    }
  }, [activeFen]);

  // Fetch eval whenever boardFen changes
  useEffect(() => {
    if (!boardFen) { setBoardEval(null); return; }
    let cancelled = false;
    setBoardEval({ loading: true });
    fetchEval(boardFen).then((r) => {
      if (cancelled) return;
      setBoardEval(r);
    }).catch(() => {
      if (cancelled) return;
      setBoardEval(null);
    });
    return () => { cancelled = true; };
  }, [boardFen]);

  // Sync status polling
  useEffect(() => {
    if (!me) return;
    let cancelled = false;
    let timer;
    async function poll() {
      try {
        const s = await fetchSyncStatus();
        if (cancelled) return;
        setSync(s);
        const fast = s.state === 'running' || (s.evalsTotal > 0 && s.evalsDone < s.evalsTotal);
        timer = setTimeout(poll, fast ? 3000 : 10000);
      } catch {
        timer = setTimeout(poll, 10000);
      }
    }
    poll();
    return () => { cancelled = true; clearTimeout(timer); };
  }, [me]);

  // Reload tree when sync finishes, OR when eval progress crosses big thresholds
  const prevSyncState = useRef(sync.state);
  const prevEvalsDone = useRef(sync.evalsDone);
  useEffect(() => {
    const finished = prevSyncState.current === 'running' && sync.state === 'idle';
    const evalsMovedAlot = sync.evalsDone - prevEvalsDone.current >= 50;
    if (finished || evalsMovedAlot) {
      setFilters((f) => ({ ...f }));
      prevEvalsDone.current = sync.evalsDone;
    }
    prevSyncState.current = sync.state;
  }, [sync.state, sync.evalsDone]);

  const nodes = data?.nodes || {};
  const activeNode = (activeFen && nodes[activeFen]) || (data?.rootFen ? nodes[data.rootFen] : null);
  const linePath = activeFen && data ? pathFromRoot(nodes, activeFen) : [];

  const canGoBack = !!(activeNode && activeNode.parent);
  const canGoNext = !!(activeNode && activeNode.children && activeNode.children.length);
  const goBack = () => {
    if (activeNode?.parent) setActiveFen(activeNode.parent);
  };
  const goNext = () => {
    if (!activeNode?.children?.length) return;
    const sorted = [...activeNode.children].sort((a, b) => (nodes[b]?.games || 0) - (nodes[a]?.games || 0));
    setActiveFen(sorted[0]);
  };

  // Keyboard nav — must run every render (cannot be after an early return).
  useEffect(() => {
    function h(e) {
      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
      if (e.key === 'ArrowLeft') { e.preventDefault(); goBack(); }
      if (e.key === 'ArrowRight') { e.preventDefault(); goNext(); }
    }
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  });

  if (me === undefined) return <div className="loading-screen">Loading<span className="dots" /></div>;
  if (me === null) return <LoginScreen />;

  const onSync = async () => {
    await triggerSync();
    setSync((s) => ({ ...s, state: 'running', progress: 0 }));
  };

  return (
    <div className="app">
      <TopBar me={me} sync={sync} onSync={onSync} onToggleFilters={() => setShowFilters((v) => !v)} />

      {showFilters && (
        <FilterBar
          filters={filters}
          setFilters={setFilters}
          onClose={() => setShowFilters(false)}
          overview={data?.overview}
        />
      )}

      <div className="simple-main">
        <aside className="leaks-col">
          <LeaksHeader sort={sort} setSort={setSort} overview={data?.overview} />
          {data ? (
            <WorstList
              worst={data.worst}
              activeFen={activeFen}
              onPick={setActiveFen}
              nodes={data.nodes}
              sort={sort}
              setSort={setSort}
            />
          ) : (
            <div className="leaks-loading">
              {dataError ? dataError : <span>Loading your games<span className="dots" /></span>}
            </div>
          )}
        </aside>

        <section className="board-panel">
          {!vendorReady ? (
            <div className="loading-screen" style={{ minHeight: 400 }}>Loading board<span className="dots" /></div>
          ) : !activeNode || !boardFen ? (
            <div className="empty-board">
              <div className="empty-sub">
                {sync.state === 'running'
                  ? `Syncing games · ${sync.progress}${sync.total ? ` of ${sync.total}` : ''}`
                  : 'Pick an opening from the list to load it on the board.'}
              </div>
            </div>
          ) : (
            <BoardPanel
              boardFen={boardFen}
              setBoardFen={setBoardFen}
              lastMove={lastMove}
              setLastMove={setLastMove}
              activeNode={activeNode}
              boardEval={boardEval}
              linePath={linePath}
              setActiveFen={setActiveFen}
              goBack={goBack}
              goNext={goNext}
              canGoBack={canGoBack}
              canGoNext={canGoNext}
              filters={filters}
            />
          )}
        </section>
      </div>
    </div>
  );
}

function TopBar({ me, sync, onSync, onToggleFilters }) {
  let syncText;
  let syncCls = 'sync-pill';
  if (sync.state === 'running') {
    syncCls += ' syncing';
    syncText = `Syncing · ${sync.progress}${sync.total ? '/' + sync.total : ''}`;
  } else if (sync.state === 'error') {
    syncCls += ' error';
    syncText = 'Sync error';
  } else {
    syncText = `Synced · ${formatSince(me.lastSyncedAt)}`;
  }

  const evalProgress = sync.evalsTotal > 0
    ? { done: sync.evalsDone, total: sync.evalsTotal, pct: Math.round((sync.evalsDone / sync.evalsTotal) * 100) }
    : null;
  const initials = (me.username || '??').slice(0, 2).toUpperCase();

  return (
    <div className="topbar">
      <div className="brand">
        <span className="brand-mark" />
        <div>
          <div className="brand-name">Fumble</div>
          <div className="brand-sub">Opening weakness explorer</div>
        </div>
      </div>

      <div className="user">
        <span className={syncCls}>
          <span className="dot" />
          {syncText}
        </span>
        {evalProgress && evalProgress.done < evalProgress.total && (
          <span className="sync-pill analyzing" title={`${evalProgress.done}/${evalProgress.total} positions analyzed`}>
            <span className="dot" />
            Analyzing · {evalProgress.done}/{evalProgress.total}
          </span>
        )}
        <button className="btn" onClick={onSync} disabled={sync.state === 'running'}>
          Sync now
        </button>
        <button className="btn btn-ghost" onClick={onToggleFilters}>Filters</button>
        <span style={{ fontSize: 13, color: 'var(--fg-dim)' }}>{me.username}</span>
        <span className="avatar">{initials}</span>
        <button className="logout-btn" onClick={logout}>Logout</button>
      </div>
    </div>
  );
}

function FilterBar({ filters, setFilters, onClose, overview }) {
  const set = (k, v) => setFilters({ ...filters, [k]: v });
  return (
    <div className="filter-bar">
      <div className="fb-group">
        <div className="fb-lbl">Color</div>
        <div className="seg">
          {['white', 'black', 'both'].map((c) => (
            <button key={c} className={filters.color === c ? 'active' : ''} onClick={() => set('color', c)}>
              {c[0].toUpperCase() + c.slice(1)}
            </button>
          ))}
        </div>
      </div>
      <div className="fb-group">
        <div className="fb-lbl">Time control</div>
        <div className="seg">
          {['bullet', 'blitz', 'rapid', 'classical'].map((t) => (
            <button
              key={t}
              className={filters.tc.includes(t) ? 'active' : ''}
              onClick={() => set('tc', filters.tc.includes(t) ? filters.tc.filter((x) => x !== t) : [...filters.tc, t])}
            >
              {t[0].toUpperCase() + t.slice(1, 4)}
            </button>
          ))}
        </div>
      </div>
      <div className="fb-group">
        <div className="fb-lbl">Date</div>
        <div className="seg">
          {['7d', '30d', '6mo', '1y', 'all'].map((d) => (
            <button key={d} className={filters.date === d ? 'active' : ''} onClick={() => set('date', d)}>
              {d}
            </button>
          ))}
        </div>
      </div>
      <div className="fb-group">
        <div className="fb-lbl">Min games / line</div>
        <input
          type="number"
          min={1}
          value={filters.minGames}
          onChange={(e) => set('minGames', Math.max(1, +e.target.value || 1))}
          style={{ width: 60 }}
        />
      </div>
      <div className="fb-group">
        <div className="fb-lbl">Rating range</div>
        <div className="range-row" style={{ width: 140 }}>
          <input type="number" value={filters.oppMin} onChange={(e) => set('oppMin', +e.target.value)} />
          <span style={{ color: 'var(--fg-muted)' }}>–</span>
          <input type="number" value={filters.oppMax} onChange={(e) => set('oppMax', +e.target.value)} />
        </div>
      </div>
      <button className="btn btn-ghost" style={{ marginLeft: 'auto' }} onClick={onClose}>Close</button>
    </div>
  );
}

function LeaksHeader({ overview }) {
  return (
    <div className="leaks-head">
      <h2>Worst openings</h2>
      {overview && (
        <div className="leaks-sub">
          {overview.games} games · {Math.round((overview.winRate || 0) * 100)}% win ·
          {' '}Ø <span className="bad">−{overview.avgOpeningCpl || 0}</span> in the opening
        </div>
      )}
    </div>
  );
}

function BoardPanel({
  boardFen, setBoardFen, lastMove, setLastMove, activeNode, boardEval,
  linePath, setActiveFen, goBack, goNext, canGoBack, canGoNext, filters,
}) {
  const onBoardMove = ({ from, to, san, fen }) => {
    setBoardFen(fen);
    setLastMove([from, to]);
  };

  const resetToActive = () => {
    setBoardFen(activeNode.fen);
    setLastMove(null);
  };

  const boardDiverged = activeNode && boardFen !== activeNode.fen;
  const wr = activeNode?.games ? (activeNode.wins + 0.5 * activeNode.draws) / activeNode.games : null;

  const full = boardFen ? toFullFen(boardFen) : '';
  // Lichess path form: slashes stay as /, spaces become _.
  // (Query form ?fen=... is silently ignored by Lichess.)
  const pathFen = full.replace(/ /g, '_');
  // Always pin orientation — without it Lichess flips to the side-to-move's POV.
  const orientation = filters.color === 'black' ? 'black' : 'white';
  const lichessHref = `https://lichess.org/analysis/standard/${pathFen}?color=${orientation}`;

  return (
    <div className="board-panel-inner">
      <div className="board-side">
        <div className="cg-wrap">
          <ChessgroundBoard
            fen={boardFen}
            flip={filters.color === 'black'}
            lastMove={lastMove}
            onMove={onBoardMove}
          />
        </div>
        <div className="board-controls">
          <button className="btn" onClick={goBack} disabled={!canGoBack} title="Previous move (←)">◀ Back</button>
          <button className="btn" onClick={goNext} disabled={!canGoNext} title="Next move (→)">Next ▶</button>
          {boardDiverged && (
            <button className="btn btn-ghost" onClick={resetToActive}>Reset</button>
          )}
          <a href={lichessHref} target="_blank" rel="noreferrer" className="btn btn-primary lichess-link-btn">
            Analyze on Lichess ↗
          </a>
        </div>
      </div>

      <div className="info-side">
        <EvalBadge boardEval={boardEval} />

        <div className="move-line-box">
          {linePath.length === 0 ? (
            <span style={{ color: 'var(--fg-muted)' }}>(starting position)</span>
          ) : linePath.map((n, i) => {
            const isActive = n.fen === activeNode?.fen;
            const prefix = n.ply % 2 === 1
              ? `${Math.ceil(n.ply / 2)}. `
              : (i === 0 ? `${Math.ceil(n.ply / 2)}… ` : '');
            return (
              <span key={n.fen}>
                {prefix && <span style={{ color: 'var(--fg-muted)' }}>{prefix}</span>}
                <span
                  className={'tok' + (isActive ? ' hl' : '')}
                  onClick={() => setActiveFen(n.fen)}
                >{n.san}</span>{' '}
              </span>
            );
          })}
        </div>

        {activeNode && (
          <div className="stats-grid">
            <Stat label="Games" value={activeNode.games} />
            <Stat label="Win rate" value={wr != null ? Math.round(wr * 100) + '%' : '—'} />
            <Stat
              label="Your Ø CPL"
              value={activeNode.myAvgLossCp != null ? '−' + activeNode.myAvgLossCp : '—'}
              bad={activeNode.myAvgLossCp >= 20}
            />
            <Stat
              label="Worst single move"
              value={activeNode.worstLossCp != null ? '−' + activeNode.worstLossCp : '—'}
              bad={activeNode.worstLossCp >= 40}
            />
          </div>
        )}

        {activeNode?.openingName && (
          <div className="callout">
            {activeNode.openingName}
            {activeNode.opening && <span className="src">ECO {activeNode.opening}</span>}
          </div>
        )}
      </div>
    </div>
  );
}

function EvalBadge({ boardEval }) {
  if (!boardEval || boardEval.loading) {
    return <div className="eval-badge loading">analyzing<span className="dots" /></div>;
  }
  if (boardEval.cp == null) return <div className="eval-badge neutral">no eval</div>;
  const cp = boardEval.cp;
  const sign = cp >= 0 ? '+' : '';
  const text = `${sign}${(cp / 100).toFixed(2)}`;
  const cls = cp >= 80 ? 'white-big' : cp <= -80 ? 'black-big' : cp >= 20 ? 'white-small' : cp <= -20 ? 'black-small' : 'even';
  const sourceText = boardEval.source === 'lichess-cloud' ? 'lichess cloud' :
                     boardEval.source === 'local-stockfish' ? `stockfish d${boardEval.depth || ''}` : boardEval.source || '';
  return (
    <div className={`eval-badge ${cls}`}>
      <span className="e-v">{text}</span>
      <span className="e-src">{sourceText}</span>
    </div>
  );
}

function Stat({ label, value, bad }) {
  return (
    <div className="stat">
      <div className="stat-lbl">{label}</div>
      <div className={'stat-val' + (bad ? ' bad' : '')}>{value}</div>
    </div>
  );
}

function LoginScreen() {
  return (
    <div className="login-screen">
      <div className="login-card">
        <span className="brand-mark" />
        <div className="brand-name">Fumble</div>
        <div className="brand-sub">Opening Weakness Explorer</div>
        <div className="login-desc">
          Sign in with your <b>Lichess</b> account to see which openings you mess up
          fastest — sorted by how much Stockfish eval you drop in the first 4–5 moves.
        </div>
        <a href="/auth/login" className="btn btn-primary">Login with Lichess</a>
        <div className="foot">read-only access via the lichess api</div>
      </div>
    </div>
  );
}

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