/* Shots screen — filmstrip view + table view + full ShotPlan editor.

   Covers all 12 PRD §12.7 fields: shot_id, scene_id, duration_seconds,
   shot_type, subject, action, camera, composition, lighting, motion, sound,
   horror_modifiers (threat_visibility, scare_type, tension_peak, final_frame).

   - Filmstrip view: shot strip + EDITABLE detail panel (every field).
   - Table view: 7 quick-edit columns. Open arrow jumps to filmstrip detail.
   - Insert shot: composer that adds to the currently-selected scene.
*/

const SHOT_TYPES = [
  "static_wide", "static_medium", "static_close", "static_low",
  "dolly_in", "dolly_back", "tracking_lat", "tight_cu", "two_shot",
  "insert", "slow_push", "handheld",
];
const VISIBILITY = ["unseen", "heard", "shadow", "reflection", "partial", "full"];
const SCARE_TYPES = ["none", "dread", "uncanny", "misdirect", "jump", "reveal", "body", "sonic"];

const ScreenShots = ({ data, onJumpToPrompt, pendingShotId, clearPendingShot }) => {
  const isLive = Boolean(data._live) && Boolean(window.CinematonAPI);
  const { shots, outline, project } = data;
  const [sel, setSel] = React.useState(shots && shots[0] ? shots[0].id : null);

  // Honor a cross-stage focus request (e.g., "Open shot" from the Script
  // page). The parent clears the pending id once we consume it so a later
  // un-focused visit doesn't keep snapping to the same shot.
  React.useEffect(() => {
    if (pendingShotId && shots.some((s) => s.id === pendingShotId)) {
      setSel(pendingShotId);
      clearPendingShot && clearPendingShot();
    }
  }, [pendingShotId, shots]);
  const [view, setView] = React.useState("strip");
  const [adding, setAdding] = React.useState(null); // scene_id of scene we're adding to
  const shot = shots.find((s) => s.id === sel) || shots[0];

  const groups = outline.map((sc) => ({
    scene: sc, shots: shots.filter((sh) => sh.scene === sc.id),
  }));

  const insertShot = (sceneId) => setAdding(sceneId);

  return (
    <div className="page" style={{ display: "grid", gridTemplateRows: "auto 1fr", height: "100%" }}>
      <PageHeader
        title="Shot List"
        sub={`Stage 05 / 09 · ${shots.length} shots · est ${fmtTime(shots.reduce((a, s) => a + s.dur, 0))} · avg ${(shots.reduce((a, s) => a + s.dur, 0) / Math.max(1, shots.length)).toFixed(1)}s`}
      >
        <div className="seg-toggle" role="tablist">
          <button className={`seg-btn ${view === "strip" ? "active" : ""}`} onClick={() => setView("strip")}>Filmstrip · edit</button>
          <button className={`seg-btn ${view === "table" ? "active" : ""}`} onClick={() => setView("table")}>Table · edit</button>
        </div>
        <button className="btn ghost" onClick={() => insertShot(shot ? shot.scene : (outline[0] && outline[0].id))}>
          <Icon name="plus"/> Insert shot
        </button>
        <button className="btn primary" onClick={() => onJumpToPrompt(sel)}>Compile prompts →</button>
      </PageHeader>

      {adding && (
        <InsertShotComposer
          sceneId={adding}
          projectId={project.id}
          isLive={isLive}
          afterScene={outline.find((o) => o.id === adding)}
          onCancel={() => setAdding(null)}
          onCreated={(id) => {
            setAdding(null);
            if (id) setSel(id);
            window.dispatchEvent(new CustomEvent("cinematon:refresh-run"));
          }}
        />
      )}

      {view === "table" ? (
        <ShotTableView shots={shots} outline={outline} sel={sel} setSel={setSel} onJumpToPrompt={onJumpToPrompt} isLive={isLive}/>
      ) : (
        <div style={{ display: "grid", gridTemplateRows: "auto 1fr", height: "100%", overflow: "hidden" }}>
          {/* Filmstrip of all shots, grouped by scene */}
          <div style={{ padding: "14px 24px", borderBottom: "1px solid var(--line-soft)", overflowX: "auto" }}>
            <div className="row" style={{ gap: 18 }}>
              {groups.map((g) => (
                <div key={g.scene.id} className="col" style={{ minWidth: g.shots.length * 170 + 60 }}>
                  <div className="row" style={{ justifyContent: "space-between", marginBottom: 6 }}>
                    <div className="mono dim" style={{ fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase" }}>
                      Scene {String(g.scene.n).padStart(2, "0")} · {g.scene.function} · {fmtTime(g.scene.dur)}
                    </div>
                    <button
                      className="btn ghost"
                      style={{ fontSize: 10, padding: "2px 8px" }}
                      onClick={() => insertShot(g.scene.id)}
                      title="Insert shot in this scene"
                    >+ shot</button>
                  </div>
                  <div className="row" style={{ gap: 8 }}>
                    {g.shots.map((s) => (
                      <ShotCard key={s.id} shot={s} selected={s.id === sel} onSelect={() => setSel(s.id)} />
                    ))}
                    {g.shots.length === 0 && (
                      <button
                        onClick={() => insertShot(g.scene.id)}
                        style={{
                          width: 160, height: 90, borderRadius: 4, border: "1px dashed var(--line)",
                          background: "transparent", color: "var(--fg-muted)", cursor: "pointer", fontSize: 12,
                        }}
                      >+ first shot</button>
                    )}
                  </div>
                </div>
              ))}
            </div>
          </div>

          {/* Detail panel with FULL editor for every PRD §12.7 field */}
          {shot && (
            <ShotDetailEditor
              shot={shot}
              outline={outline}
              isLive={isLive}
              onJumpToPrompt={onJumpToPrompt}
            />
          )}
        </div>
      )}
    </div>
  );
};

const ShotCard = ({ shot, selected, onSelect }) => (
  <div className={`shot ${selected ? "selected" : ""}`} onClick={onSelect}>
    <div className="thumb">
      <div className="thumb-placeholder" data-label={shot.type.replace("_", " ")} />
      <div className="thumb-noir"/>
      <div style={{ position: "absolute", top: 4, left: 6, color: "var(--accent)", fontFamily: "var(--font-mono)", fontSize: 9 }}>{shot.id}</div>
      <div style={{ position: "absolute", bottom: 4, right: 6, color: "var(--fg-muted)", fontFamily: "var(--font-mono)", fontSize: 9 }}>{shot.dur}s</div>
    </div>
    <div className="meta">
      <div className="desc">{shot.desc}</div>
      {shot._action && (
        <div style={{ marginTop: 3, fontSize: 11, color: "var(--fg-muted)", fontStyle: "italic", display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}>
          {shot._action}
        </div>
      )}
      <div className="row" style={{ marginTop: 4, fontFamily: "var(--font-mono)", fontSize: 9.5, color: "var(--fg-faint)", justifyContent: "space-between" }}>
        <span>{shot.type.split("_")[0]}</span>
        <span>{shot.visibility}</span>
      </div>
    </div>
  </div>
);

// ---- Detail panel: full editable ShotPlan ----
const ShotDetailEditor = ({ shot, outline, isLive, onJumpToPrompt }) => {
  // Resolve scene context once.
  const scene = outline.find((o) => o.id === shot.scene);

  // PATCH a single field. The shot object on screen carries SAMPLE-shaped
  // names (desc, type, dur, visibility); the apiField argument maps to the
  // real PRD ShotPlan field name on the server.
  const patch = (apiField, value) => {
    if (!isLive || !window.CinematonAPI) return;
    const body = setNestedPatch(apiField, value);
    return window.CinematonAPI.patchShot(shot.id, body)
      .catch((err) => console.error(`shot ${apiField} patch failed`, err));
  };

  const sectionTitle = (s) => (
    <div className="card-title" style={{ marginBottom: 6 }}>{s}</div>
  );

  // Read horror modifiers via either the SAMPLE flat shape OR the live nested shape.
  // Live data adapter writes shot.visibility from horror_modifiers.threat_visibility.
  // Fixture sample uses shot.visibility directly. For other modifiers we need to fall back.
  const hm = (shot._horror_modifiers || {});
  const scareType = hm.scare_type || "dread";
  const tensionPeak = hm.tension_peak || 5;
  const finalFrame = hm.final_frame || "";

  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 360px", height: "100%", overflow: "hidden" }}>
      <div className="scroll" style={{ padding: 24 }}>
        <div className="row" style={{ justifyContent: "space-between", alignItems: "flex-end", marginBottom: 16 }}>
          <div style={{ minWidth: 0, flex: 1 }}>
            <div className="mono dim" style={{ fontSize: 11 }}>SHOT · {shot.id} · scene {shot.scene}</div>
            <h2 className="serif" style={{ fontSize: 24, margin: "4px 0 0" }}>
              <EditableField value={shot.desc} onSave={(v) => patch("subject", v)} serif/>
            </h2>
            {shot._action && (
              <div className="serif" style={{ fontSize: 14, color: "var(--fg-muted)", marginTop: 4, fontStyle: "italic" }}>
                {shot._action}
              </div>
            )}
          </div>
          <div className="row gap-sm">
            <span className="tag">{shot.dur}s</span>
            <span className="tag accent">scene {shot.scene}</span>
          </div>
        </div>

        {/* Big preview placeholder */}
        <div style={{ position: "relative", height: 280, borderRadius: 6, border: "1px solid var(--line)", overflow: "hidden", marginBottom: 18 }}>
          <div className="thumb-placeholder" data-label={`PREVIEW · ${shot.type}`} />
          <div className="thumb-noir"/>
          <div style={{ position: "absolute", bottom: 12, left: 12, right: 12, display: "flex", justifyContent: "space-between" }}>
            <span className="mono" style={{ fontSize: 11, color: "var(--accent)" }}>● REC · {shot.id}</span>
            <span className="mono" style={{ fontSize: 11, color: "var(--fg-muted)" }}>{shot.dur}s</span>
          </div>
        </div>

        {/* Identifiers / scalar fields */}
        <div className="card" style={{ marginBottom: 14 }}>
          {sectionTitle("Identifiers")}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 10 }}>
            <FieldLabeled label="Shot ID"  value={shot.id} mono readOnly/>
            <FieldLabeled label="Scene ID" value={shot.scene} mono readOnly/>
            <FieldLabeled label="Duration (s)" value={shot.dur} numeric onSave={(v) => patch("duration_seconds", Number(v) || 0)}/>
            <FieldLabeled label="Type" select options={SHOT_TYPES} value={shot.type} onSave={(v) => patch("shot_type", v)}/>
            <FieldLabeled label="Visibility" select options={VISIBILITY} value={shot.visibility} onSave={(v) => patch("horror_modifiers.threat_visibility", v)}/>
          </div>
        </div>

        {/* Cinematic content — every PRD §12.7 narrative field */}
        <div className="card" style={{ marginBottom: 14 }}>
          {sectionTitle("Cinematic content")}
          <div className="col gap-md">
            <FieldStack label="Subject" value={shot.desc} onSave={(v) => patch("subject", v)} multiline/>
            <FieldStack label="Action"  value={shot._action || ""} placeholder="What the subject is doing in the shot" onSave={(v) => patch("action", v)} multiline/>
            <FieldStack label="Camera"  value={shot.camera} onSave={(v) => patch("camera", v)}/>
            <FieldStack label="Composition" value={shot._composition || ""} placeholder="Framing, negative space, blocking" onSave={(v) => patch("composition", v)}/>
            <FieldStack label="Lighting" value={shot._lighting || ""} placeholder="Sources, motivation, color, intensity" onSave={(v) => patch("lighting", v)} multiline/>
            <FieldStack label="Motion"   value={shot._motion || ""} placeholder="Subject + camera motion through the shot" onSave={(v) => patch("motion", v)} multiline/>
            <FieldStack label="Sound"    value={shot._sound || ""} placeholder="Diegetic sound, room tone, sting cues" onSave={(v) => patch("sound", v)}/>
          </div>
        </div>

        {/* Horror modifiers — PRD §12.7 horror_modifiers sub-object */}
        <div className="card">
          {sectionTitle("Horror modifiers · genre plugin")}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 10 }}>
            <FieldLabeled label="Threat visibility" select options={VISIBILITY} value={shot.visibility} onSave={(v) => patch("horror_modifiers.threat_visibility", v)}/>
            <FieldLabeled label="Scare type" select options={SCARE_TYPES} value={scareType} onSave={(v) => patch("horror_modifiers.scare_type", v)}/>
            <FieldLabeled label="Tension peak (1-9)" numeric value={tensionPeak} onSave={(v) => patch("horror_modifiers.tension_peak", Math.max(1, Math.min(9, Number(v) || 1)))}/>
            <FieldLabeled label="Final frame" value={finalFrame} placeholder="The image you remember" onSave={(v) => patch("horror_modifiers.final_frame", v)}/>
          </div>
        </div>
      </div>

      {/* Right panel — scene context + jump links */}
      <div className="panel right">
        <div className="panel-header"><div className="htitle">Scene context</div></div>
        <div style={{ padding: 16 }}>
          {scene && (
            <div className="col gap-md">
              <div>
                <div className="card-title">Beat</div>
                <p className="serif" style={{ margin: 0 }}>{scene.beat}</p>
              </div>
              <div>
                <div className="card-title">Tension peak {Math.max(...scene.curve)}</div>
                <div style={{ height: 38, background: "var(--bg-inset)", borderRadius: 3, padding: 5 }}>
                  <TensionCurve points={scene.curve} height={28} width={300}/>
                </div>
              </div>
              <div>
                <div className="card-title">Final frame · scene</div>
                <p className="serif" style={{ color: "var(--accent)", margin: 0 }}>"{scene.finalframe}"</p>
              </div>
              <hr className="hr"/>
              <button className="btn primary" onClick={() => onJumpToPrompt(shot.id)}>Open prompt packet →</button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

// Generic labeled field (compact, for grid)
const FieldLabeled = ({ label, value, onSave, mono, numeric, select, options, readOnly, placeholder }) => (
  <div className="col" style={{ gap: 3 }}>
    <label style={{ fontSize: 9.5, color: "var(--fg-faint)", letterSpacing: "0.06em", textTransform: "uppercase", fontFamily: "var(--font-mono)" }}>{label}</label>
    {readOnly || !onSave ? (
      <div className="input" style={{ padding: "6px 8px", fontSize: 12, fontFamily: mono ? "var(--font-mono)" : "var(--font-sans)", opacity: 0.85, color: "var(--fg-muted)" }}>{String(value ?? "")}</div>
    ) : select ? (
      <SelectField value={value} options={options} onSave={onSave}/>
    ) : (
      <EditableField value={value} mono={mono || numeric} placeholder={placeholder} onSave={onSave}/>
    )}
  </div>
);

// Stacked field (label on top, full-width input/textarea)
const FieldStack = ({ label, value, onSave, multiline, placeholder }) => (
  <div className="field">
    <label>{label}</label>
    <EditableField value={value} multiline={multiline} autosize={multiline} placeholder={placeholder} onSave={onSave}/>
  </div>
);

// SelectField wrapped to behave like EditableField (for consistency).
const SelectField = ({ value, options, onSave }) => {
  const [v, setV] = React.useState(value);
  React.useEffect(() => { setV(value); }, [value]);
  return (
    <select
      className="input"
      value={v}
      onChange={(e) => { setV(e.target.value); onSave(e.target.value); }}
      style={{ padding: "6px 8px", fontSize: 12 }}
    >
      {options.map((o) => <option key={o} value={o}>{o}</option>)}
    </select>
  );
};

// ---- Insert shot composer ----
const InsertShotComposer = ({ sceneId, projectId, isLive, afterScene, onCancel, onCreated }) => {
  const [subject, setSubject] = React.useState("");
  const [action, setAction] = React.useState("");
  const [duration, setDuration] = React.useState(6);
  const [shotType, setShotType] = React.useState("static_medium");
  const [creating, setCreating] = React.useState(false);
  const [error, setError] = React.useState(null);

  const create = async () => {
    if (!subject.trim()) { setError("Subject is required."); return; }
    setCreating(true); setError(null);
    if (!isLive || !window.CinematonAPI) {
      // Fixture mode: dispatch event for SAMPLE consumers; we can't really
      // append to data.shots without state lifting. Just notify and close.
      setCreating(false);
      onCreated && onCreated(null);
      return;
    }
    try {
      const created = await window.CinematonAPI.addShot(projectId, {
        scene_id: sceneId,
        duration_seconds: Number(duration) || 6,
        shot_type: shotType,
        subject: subject.trim(),
        action: action.trim() || subject.trim(),
        camera: "static, eye-level",
        composition: "subject centered, balanced negative space",
        lighting: "motivated practical lighting",
        motion: "static",
        sound: "low room tone",
        horror_modifiers: { threat_visibility: "unseen", scare_type: "dread", tension_peak: 4, final_frame: "" },
      });
      onCreated && onCreated(created.id);
    } catch (err) {
      setError(err.message || "create failed");
    } finally { setCreating(false); }
  };

  return (
    <div style={{ padding: 14, borderBottom: "1px solid var(--accent-dim)", background: "oklch(0.18 0.04 70 / 0.18)" }}>
      <div className="row" style={{ marginBottom: 10, gap: 12, alignItems: "baseline" }}>
        <span className="card-title" style={{ margin: 0 }}>Insert shot · scene {sceneId}</span>
        {afterScene && <span className="dim mono" style={{ fontSize: 10 }}>{afterScene.title}</span>}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 100px 140px auto", gap: 10, alignItems: "end" }}>
        <div className="col" style={{ gap: 3 }}>
          <label style={{ fontSize: 10, color: "var(--fg-faint)", textTransform: "uppercase", fontFamily: "var(--font-mono)" }}>Subject *</label>
          <input className="input" placeholder="A nurse at the end of a dark corridor" value={subject} onChange={(e) => setSubject(e.target.value)} autoFocus/>
        </div>
        <div className="col" style={{ gap: 3 }}>
          <label style={{ fontSize: 10, color: "var(--fg-faint)", textTransform: "uppercase", fontFamily: "var(--font-mono)" }}>Action</label>
          <input className="input" placeholder="What is happening in the shot" value={action} onChange={(e) => setAction(e.target.value)}/>
        </div>
        <div className="col" style={{ gap: 3 }}>
          <label style={{ fontSize: 10, color: "var(--fg-faint)", textTransform: "uppercase", fontFamily: "var(--font-mono)" }}>Dur (s)</label>
          <input className="input mono" type="number" min={1} max={12} value={duration} onChange={(e) => setDuration(e.target.value)}/>
        </div>
        <div className="col" style={{ gap: 3 }}>
          <label style={{ fontSize: 10, color: "var(--fg-faint)", textTransform: "uppercase", fontFamily: "var(--font-mono)" }}>Type</label>
          <select className="input" value={shotType} onChange={(e) => setShotType(e.target.value)}>
            {SHOT_TYPES.map((t) => <option key={t}>{t}</option>)}
          </select>
        </div>
        <div className="row gap-sm">
          <button className="btn ghost" onClick={onCancel} disabled={creating}>Cancel</button>
          <button className="btn primary" onClick={create} disabled={creating || !subject.trim()}>
            {creating ? "Creating..." : "Insert"}
          </button>
        </div>
      </div>
      {error && <div className="tag bad" style={{ marginTop: 8 }}>{error}</div>}
    </div>
  );
};

// ============================================================================
// Table view — fast-edit grid of all shots, expanded with Action column
// ============================================================================
const ShotTableView = ({ shots, outline, sel, setSel, onJumpToPrompt, isLive }) => {
  const rows = [];
  outline.forEach((sc) => {
    const sceneShots = shots.filter((s) => s.scene === sc.id);
    if (sceneShots.length === 0) return;
    rows.push({ kind: "scene", sc });
    sceneShots.forEach((sh) => rows.push({ kind: "shot", sh }));
  });

  return (
    <div className="scroll" style={{ padding: "16px 24px", overflow: "auto" }}>
      <div className="row" style={{ marginBottom: 12, justifyContent: "space-between" }}>
        <div className="dim mono" style={{ fontSize: 10 }}>
          INLINE EDIT · CLICK A CELL · SAVES ON BLUR · OPEN ARROW JUMPS TO FULL EDITOR
        </div>
        <div className="row gap-sm">
          <span className="tag">{shots.length} shots</span>
        </div>
      </div>

      <table className="shot-table">
        <thead>
          <tr>
            <th>Thumb</th>
            <th>Shot ID</th>
            <th>Type</th>
            <th>Subject</th>
            <th>Action</th>
            <th>Camera</th>
            <th style={{ textAlign: "right" }}>Dur</th>
            <th>Visibility</th>
            <th style={{ width: 100 }}></th>
          </tr>
        </thead>
        <tbody>
          {rows.map((r) => {
            if (r.kind === "scene") {
              return (
                <tr key={"sc-" + r.sc.id} style={{ background: "var(--bg-inset)" }}>
                  <td colSpan={9} style={{ padding: "8px 12px", borderBottom: "1px solid var(--line)" }}>
                    <div className="row" style={{ gap: 12 }}>
                      <span className="mono" style={{ fontSize: 10, color: "var(--accent)", letterSpacing: "0.06em" }}>
                        SCENE {String(r.sc.n).padStart(2, "0")}
                      </span>
                      <span className="serif" style={{ fontSize: 14 }}>{r.sc.title}</span>
                      <span className="mono dim" style={{ fontSize: 10, marginLeft: "auto" }}>
                        {fmtTime(r.sc.dur)} · {r.sc.function}
                      </span>
                    </div>
                  </td>
                </tr>
              );
            }
            const sh = r.sh;
            const isSel = sh.id === sel;
            return (
              <tr key={sh.id} className={isSel ? "selected" : ""} onClick={() => setSel(sh.id)}>
                <td className="thumb-td">
                  <div className="mini-thumb">
                    <div className="thumb-placeholder" data-label={sh.type.split("_")[0]}/>
                    <div className="thumb-noir"/>
                  </div>
                </td>
                <td className="id-td">
                  {sh.id}
                  {sh._prompt_stale && (
                    <span className="tag warn" style={{ fontSize: 9, marginLeft: 6 }} title="Prompt packet is out of date relative to this shot's fields. Click ↻ to regenerate.">stale</span>
                  )}
                </td>
                <td className="type-td">
                  <ShotCellSelect shot={sh} field="type" apiField="shot_type" isLive={isLive} options={SHOT_TYPES}/>
                </td>
                <td className="desc-td">
                  <ShotCellInput shot={sh} field="desc" apiField="subject" isLive={isLive}/>
                </td>
                <td className="desc-td">
                  <ShotCellInput shot={sh} field="_action" apiField="action" isLive={isLive} placeholder="action..."/>
                </td>
                <td className="desc-td">
                  <ShotCellInput shot={sh} field="camera" apiField="camera" isLive={isLive} style={{ fontSize: 11.5, color: "var(--fg-muted)" }}/>
                </td>
                <td className="dur-td">
                  <ShotCellInput shot={sh} field="dur" apiField="duration_seconds" isLive={isLive} numeric style={{ fontFamily: "var(--font-mono)", textAlign: "right", width: 50, fontSize: 11 }}/>
                </td>
                <td className="vis-td">
                  <div className="row" style={{ gap: 6 }}>
                    <Aperture value={sh.visibility} size={14}/>
                    <ShotCellSelect shot={sh} field="visibility" apiField="horror_modifiers.threat_visibility" isLive={isLive} options={VISIBILITY} style={{ fontSize: 11 }}/>
                  </div>
                </td>
                <td>
                  <div className="row gap-sm">
                    <button className="btn ghost" style={{ fontSize: 10, padding: "3px 8px" }} onClick={(e) => { e.stopPropagation(); onJumpToPrompt(sh.id); }}>Open →</button>
                    {isLive && (
                      <button
                        title="Regenerate prompt packet from current shot data"
                        onClick={async (e) => {
                          e.stopPropagation();
                          try { await window.CinematonAPI.regenShotPrompt(sh.id); window.dispatchEvent(new CustomEvent("cinematon:refresh-run")); }
                          catch (err) { console.error("regen prompt failed", err); alert("Regen failed: " + (err.message || err)); }
                        }}
                        className="btn ghost" style={{ fontSize: 10, padding: "3px 6px" }}>↻</button>
                    )}
                    {isLive && (
                      <button
                        title="Delete shot"
                        onClick={async (e) => {
                          e.stopPropagation();
                          if (!confirm(`Delete shot ${sh.id}?`)) return;
                          try { await window.CinematonAPI.deleteShot(sh.id); window.dispatchEvent(new CustomEvent("cinematon:refresh-run")); }
                          catch (err) { console.error("shot delete failed", err); }
                        }}
                        style={{ background: "transparent", border: "none", color: "var(--fg-faint)", cursor: "pointer", fontSize: 12 }}>⌫</button>
                    )}
                  </div>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
};

// ---- inline cell editors that fire PATCH /api/shots/:id ----
const ShotCellInput = ({ shot, field, apiField, isLive, numeric, style, placeholder }) => {
  const [val, setVal] = React.useState(shot[field] ?? "");
  const [dirty, setDirty] = React.useState(false);
  React.useEffect(() => { if (!dirty) setVal(shot[field] ?? ""); }, [shot, field, dirty]);

  const commit = async () => {
    if (!dirty) return;
    setDirty(false);
    if (!isLive) return;
    const value = numeric ? Number(val) : val;
    const patch = setNestedPatch(apiField, value);
    try { await window.CinematonAPI.patchShot(shot.id, patch); }
    catch (err) { console.error("shot patch failed", err); }
  };

  return (
    <input
      className="cell-edit"
      value={val}
      placeholder={placeholder}
      onChange={(e) => { setVal(e.target.value); setDirty(true); }}
      onBlur={commit}
      onKeyDown={(e) => { if (e.key === "Enter") e.target.blur(); if (e.key === "Escape") { setVal(shot[field] ?? ""); setDirty(false); e.target.blur(); } }}
      onClick={(e) => e.stopPropagation()}
      style={style}
    />
  );
};

const ShotCellSelect = ({ shot, field, apiField, isLive, options, style }) => {
  const onChange = async (e) => {
    const value = e.target.value;
    if (!isLive) return;
    const patch = setNestedPatch(apiField, value);
    try { await window.CinematonAPI.patchShot(shot.id, patch); }
    catch (err) { console.error("shot patch failed", err); }
  };
  return (
    <select className="cell-edit" defaultValue={shot[field]} onChange={onChange} onClick={(e) => e.stopPropagation()} style={style}>
      {options.map((o) => <option key={o}>{o}</option>)}
    </select>
  );
};

function setNestedPatch(path, value) {
  const parts = path.split(".");
  if (parts.length === 1) return { [path]: value };
  const root = {};
  let cur = root;
  for (let i = 0; i < parts.length - 1; i++) {
    cur[parts[i]] = {};
    cur = cur[parts[i]];
  }
  cur[parts[parts.length - 1]] = value;
  return root;
}

window.ScreenShots = ScreenShots;
