/* global React */
// v1060 — the status bar for the per-shot frame extraction.
//
// Hugo: "i want to see a status bar, what shots are being written now etc."
// The job runs for 20+ minutes on a 2GB master, so a spinner is useless — this
// names the phase, the count, and the shot it is on RIGHT NOW, plus the last few
// it finished.
//
// Polls 1s while running, 5s when idle. It keeps polling after a reload because
// the job lives on the SERVER, not in this component — closing the page does not
// stop it, and coming back shows it still going.
function EditFramesBar({ reviewId }) {
  const [st, setSt] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState("");
  const fetcher = window.authFetch || fetch;

  const load = React.useCallback(() => {
    fetcher("/api/edit-frames/status")
      .then(r => (r.ok ? r.json() : null))
      .then(j => { if (j) setSt(j); })
      .catch(() => {});
  }, []);

  React.useEffect(() => {
    load();
    // Deliberately a timer, not requestAnimationFrame: rAF is dead in a hidden
    // tab, and this is exactly the job you leave running while you do something
    // else. A 1s poll of a tiny JSON costs nothing.
    const running = st && st.state === "running";
    const id = setInterval(load, running ? 1000 : 5000);
    return () => clearInterval(id);
  }, [load, st && st.state]);

  const post = (url, body) => {
    setBusy(true); setErr("");
    return fetcher(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body || {}) })
      .then(async r => { const j = await r.json().catch(() => ({})); if (!r.ok) throw new Error(j.error || ("HTTP " + r.status)); return j; })
      .then(j => { setSt(j.state ? j : null); load(); })
      .catch(e => { setErr(e.message); if (window.__toast) window.__toast(e.message, false); })
      .then(() => setBusy(false));
  };

  if (!st) return null;
  const running = st.state === "running";
  const pct = Math.max(0, Math.min(100, st.pct || 0));

  const PHASE = {
    starting:  "Starting",
    measuring: "Measuring the frame",
    reading:   "Reading shot numbers",
    cuts:      "Finding the exact cuts",
    writing:   "Writing frames",
    done:      "Finished",
  };
  const phase = PHASE[st.phase] || st.phase || st.state;

  return (
    <div className={"efb" + (running ? " is-running" : "")}>
      <div className="efb-head">
        <span className="efb-title">Frames from the edit</span>
        {st.title ? <span className="efb-edit">{st.title}</span> : null}
        <span className="efb-spacer"/>
        {running ? (
          <button type="button" className="efb-btn efb-btn--stop" disabled={busy}
            onClick={() => post("/api/edit-frames/cancel")}>Stop</button>
        ) : (
          <React.Fragment>
            <button type="button" className="efb-btn" disabled={busy || !reviewId}
              title="Find every shot and its cut points, but write nothing"
              onClick={() => post("/api/edit-frames/run", { review_id: reviewId, dry_run: true })}>Dry run</button>
            <button type="button" className="efb-btn efb-btn--go" disabled={busy || !reviewId}
              title="Write the first and last frame of every shot that has a number on screen"
              onClick={() => post("/api/edit-frames/run", { review_id: reviewId })}>Extract frames</button>
          </React.Fragment>
        )}
      </div>

      {/* The track is always rendered, so the card never changes height when a
          run starts or stops (invariant #20). */}
      <div className="efb-track"><div className="efb-fill" style={{ width: pct + "%" }}/></div>

      <div className="efb-line">
        <span className="efb-phase">{phase}</span>
        <span className="efb-count">{st.total ? st.done + " / " + st.total : "—"}</span>
        <span className="efb-now">{st.current ? (st.phase === "writing" ? "writing " + st.current : st.current) : " "}</span>
      </div>

      <div className="efb-line efb-line--sub">
        <span>{st.shots_found || 0} shots with a number</span>
        <span>{st.written || 0} written</span>
        {st.already_done ? <span>{st.already_done} already done</span> : null}
        {st.skipped ? <span>{st.skipped} skipped</span> : null}
        {st.errors ? <span className="efb-bad">{st.errors} problem{st.errors === 1 ? "" : "s"}</span> : null}
        {st.ocr ? <span className="efb-dim">{st.ocr.calls} reads, {st.ocr.cached} cached</span> : null}
      </div>

      {st.recent && st.recent.length ? (
        <div className="efb-recent">
          {st.recent.map(r => (
            <span key={r.shot} className="efb-chip" title={(r.status || "") + (r.len ? " · " + r.len + "s" : "")}>
              {r.shot}<i>{r.ver}</i>
            </span>
          ))}
        </div>
      ) : null}

      {st.error ? <div className="efb-err">{st.error}</div> : null}
      {err ? <div className="efb-err">{err}</div> : null}
    </div>
  );
}
window.EditFramesBar = EditFramesBar;
