// UpdatesPage — v07zz557. The daily / weekly ACTIVITY DIGEST as its OWN nav page
// (Hugo: "I asked those updates to be a DIFFERENT PAGE" — it originally rendered as a
// panel at the top of the Review page, v07zz549, burying the Edits strip). Lists
// everything that changed in the window (new heroes / WIP frames / video WIPs /
// status changes / notes / comments) across the active episode's shots, each with an
// "Open shot" button (opens the modal AT that exact version via the pending-version
// hook in App.jsx + ShotDetailModal) and a "Copy update for team" button (plain text,
// grouped by shot) so Hugo can paste a "here's where to look" summary into Slack / email.
// Data: GET /api/updates. Nav gate: nav_updates permission (invariant #21).
// The section reuses the review-view--v2 container class so all the .review-panel /
// .ru-* styles in styles/review.css apply unchanged.
const RU_STAGE = { "PROMPT": "Prompt", "FIRST-PASS": "First Pass", "CONCEPT-WIP": "Frame WIP", "CONCEPT-APPROVED": "Frame Hero", "VIDEO-WIP": "Video WIP", "VIDEO-APPROVED": "Video Hero", "UPSCALED": "4K Upscale", "PENDING": "Pending", "ARCHIVE": "Archive" };
const RU_ICON = { hero_frame: "★", hero_action: "★", wip_frame: "◐", video_wip: "▶", status: "⇢", note: "✎", comment: "❝" };
// Filter chips — slice the digest so it stays a "brief" (263 raw rows in a busy week is not).
const RU_GROUPS = [
  { key: "all",    label: "All",          types: null },
  { key: "hero",   label: "★ Heroes",     types: ["hero_frame", "hero_action"] },
  { key: "video",  label: "▶ Videos",     types: ["video_wip"] },
  { key: "status", label: "⇢ Status",     types: ["status"] },
  { key: "wip",    label: "◐ WIP frames", types: ["wip_frame"] },
  { key: "notes",  label: "✎ Notes",      types: ["note", "comment"] },
];
function _ruParseAt(s) {
  if (!s) return null;
  let t = String(s).trim().replace(" ", "T");
  if (!/[Zz]|[+\-]\d\d:?\d\d$/.test(t.slice(10))) t += "Z";   // DB times are UTC
  const d = new Date(t);
  return isNaN(d.getTime()) ? null : d;
}
function _ruWhen(s) {
  const d = _ruParseAt(s); if (!d) return "";
  const diff = Math.max(0, Date.now() - d.getTime());
  const m = Math.floor(diff / 60000), h = Math.floor(m / 60), day = Math.floor(h / 24);
  if (m < 1) return "just now";
  if (m < 60) return m + "m ago";
  if (h < 24) return h + "h ago";
  if (day < 7) return day + "d ago";
  try { return d.toLocaleDateString(undefined, { month: "short", day: "numeric" }); } catch (_) { return ""; }
}
function _ruStageLabel(ss) {
  try {
    const obj = (typeof ss === "string") ? JSON.parse(ss || "{}") : (ss || {});
    const code = window.getCurrentStage ? window.getCurrentStage({ stage_status: obj }) : null;
    return (code && (RU_STAGE[code] || code)) || "updated";
  } catch (_) { return "updated"; }
}
function _ruEventText(ev) {
  const v = ev.version ? (" " + ev.version) : "";
  switch (ev.type) {
    case "hero_frame":
    case "hero_action": return "Heroed frame" + v;
    case "wip_frame":   return "New WIP frame" + v;
    case "video_wip":   return "New video WIP" + v;
    case "status":      return "Status → " + _ruStageLabel(ev.stageStatus);
    case "note":        return "Note added" + (ev.detail ? ": " + ev.detail : "");
    case "comment":     return "Review comment" + (ev.detail ? ": " + ev.detail : "");
    default:            return ev.label || "Update";
  }
}
function UpdatesPage() {
  const [win, setWin]       = React.useState(() => { try { return localStorage.getItem("filmtracker.updates-window") || "week"; } catch (_) { return "week"; } });
  const [events, setEvents] = React.useState(null);   // null = loading
  const [err, setErr]       = React.useState(null);
  const [copied, setCopied] = React.useState(false);
  const [filter, setFilter] = React.useState(() => { try { return localStorage.getItem("filmtracker.updates-filter") || "all"; } catch (_) { return "all"; } });
  const pickWin = (w) => { setWin(w); try { localStorage.setItem("filmtracker.updates-window", w); } catch (_) {} };
  const pickFilter = (fk) => { setFilter(fk); try { localStorage.setItem("filmtracker.updates-filter", fk); } catch (_) {} };

  // Window boundary computed in the user's LOCAL time (today's midnight, or 7 days back),
  // sent as a UTC ISO string; the server normalises both sides with datetime().
  const since = React.useMemo(() => {
    const d = new Date(); d.setHours(0, 0, 0, 0);
    if (win === "week") d.setDate(d.getDate() - 6);   // rolling 7 days incl. today
    return d.toISOString().replace(/\.\d+Z$/, "Z");
  }, [win]);

  React.useEffect(() => {
    let dead = false;
    setEvents(null); setErr(null);
    let epId = ""; try { epId = localStorage.getItem("frameflow-active-episode") || ""; } catch (_) {}
    const f = window.authFetch || fetch;
    const url = "/api/updates?since=" + encodeURIComponent(since) + (epId ? "&episode=" + encodeURIComponent(epId) : "");
    f(url).then(r => r.ok ? r.json() : Promise.reject(new Error("HTTP " + r.status)))
      .then(j => { if (!dead) setEvents(Array.isArray(j.events) ? j.events : []); })
      .catch(e => { if (!dead) setErr(e.message); });
    return () => { dead = true; };
  }, [since]);

  const counts = React.useMemo(() => { const c = {}; (events || []).forEach(e => { c[e.type] = (c[e.type] || 0) + 1; }); return c; }, [events]);
  const groupCount = (g) => g.types ? g.types.reduce((n, t) => n + (counts[t] || 0), 0) : (events || []).length;
  const shown = React.useMemo(() => {
    if (!events) return null;
    const g = RU_GROUPS.find(x => x.key === filter);
    if (!g || !g.types) return events;
    const set = new Set(g.types);
    return events.filter(e => set.has(e.type));
  }, [events, filter]);

  const openShot = (ev) => {
    try {
      // v07zz557 — pass the event's FAMILY so video events land on the "video-" slot
      // (a bare "v002" would otherwise select frame v002 instead of video v002).
      if (window.__nav && window.__nav.openShot) window.__nav.openShot(ev.shot_id, ev.version, ev.type === "video_wip" ? "video" : "frame");
      else if (window.__navigate) window.__navigate("shots");
    } catch (_) {}
  };

  const copyForTeam = () => {
    const list = shown || [];
    if (!list.length) return;
    const order = [], byShot = new Map();
    for (const ev of list) {
      if (!byShot.has(ev.shot_id)) { byShot.set(ev.shot_id, { id: ev.shot_id, title: ev.shot_title, lines: [] }); order.push(ev.shot_id); }
      byShot.get(ev.shot_id).lines.push("  • " + _ruEventText(ev));
    }
    let proj = "Project";
    // 15 Sep 2026 — window.__projects is { active, projects:[…] }: read the ACTIVE row (the old `[0]` was always undefined).
    try { proj = (window.__appData && window.__appData.project && (window.__appData.project.display_name || window.__appData.project.title)) || ((window.__activeProjectRow && window.__activeProjectRow()) || {}).name || "Project"; } catch (_) {}
    const _g = RU_GROUPS.find(x => x.key === filter);
    const scope = (win === "today" ? "today" : "this week") + (_g && _g.key !== "all" ? " · " + _g.label.replace(/^[^A-Za-z]+\s*/, "") : "");
    const head = proj + " — updates (" + scope + ")";
    const body = order.map(k => { const g = byShot.get(k); return (g.id + (g.title ? " · " + g.title : "")) + "\n" + g.lines.join("\n"); }).join("\n\n");
    const text = head + "\n\n" + body;
    try { navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1600); }).catch(() => {}); } catch (_) {}
  };

  return (
    <section className="view-page review-view--v2 updates-view">
      <div className="review-panel review-updates">
        <div className="review-panel-head ru-head">
          <span className="ru-head-title">Updates</span>
          <span className="ru-toggle" role="group" aria-label="Time window">
            <button type="button" className={"ru-toggle-btn" + (win === "today" ? " is-on" : "")} onClick={() => pickWin("today")}>Today</button>
            <button type="button" className={"ru-toggle-btn" + (win === "week" ? " is-on" : "")} onClick={() => pickWin("week")}>This week</button>
          </span>
          <button type="button" className={"ru-copy" + (copied ? " is-copied" : "")} disabled={!shown || shown.length === 0} onClick={copyForTeam}
            title="Copy a plain-text summary grouped by shot — paste it to the team">
            {copied ? "Copied ✓" : "Copy update for team"}
          </button>
        </div>
        {events && events.length > 0 && (
          <div className="ru-filters" role="group" aria-label="Filter updates by type">
            {RU_GROUPS.map(g => { const n = groupCount(g); if (g.key !== "all" && n === 0) return null; return (
              <button key={g.key} type="button" className={"ru-chip" + (filter === g.key ? " is-on" : "")} onClick={() => pickFilter(g.key)}>
                {g.label} <span className="ru-chip-n">{n}</span>
              </button>
            ); })}
          </div>
        )}
        {err ? (
          <div className="ru-empty ru-empty--err">Couldn’t load updates: {err}</div>
        ) : events === null ? (
          <div className="ru-empty">Loading…</div>
        ) : (shown || []).length === 0 ? (
          <div className="ru-empty">No {filter === "all" ? "activity" : "matching updates"} {win === "today" ? "today" : "this week"}.</div>
        ) : (
          <div className="ru-list">
            {shown.map((ev, i) => (
              <div key={i} className={"ru-row ru-row--" + ev.type}>
                <span className="ru-icon" aria-hidden="true">{RU_ICON[ev.type] || "•"}</span>
                <div className="ru-main">
                  <div className="ru-label">{_ruEventText(ev)}</div>
                  <div className="ru-meta">
                    <span className="ru-shot">{ev.shot_id}{ev.shot_title ? " · " + ev.shot_title : ""}</span>
                    {ev.actor ? <span className="ru-actor">{ev.actor}</span> : null}
                    <span className="ru-when">{_ruWhen(ev.at)}</span>
                  </div>
                </div>
                <button type="button" className="ru-open" onClick={() => openShot(ev)} title="Open this shot at this version">Open shot</button>
              </div>
            ))}
          </div>
        )}
      </div>
    </section>
  );
}
window.UpdatesPage = UpdatesPage;
