/* EditorialMaterial — Editorial → Link Review → Material (E3b, 0136).
   Assigns window.LedgerEditorialMaterial; LinkReview (EditorialReview.jsx) draws it as a tab.

   Once an episode → matter link is confirmed, the matter's material is proposed as what the
   episode used: received asset requests, the EVO case folder, Iconik assets (rules in 0136).
   A person who knows the episode confirms what it actually used. That is what the request
   page's "Used in" and the body-cam exclusivity question (Decision 5) read.

   Grouped by show, then episode: "did this episode use these?" is one judgement. Confirm or
   reject per row, or every row of an episode at once. No sweep across episodes: every
   proposal here is a guess from the matter (confidence 0.40-0.80), not from the episode.
   Same gate as links: decide_piece_usage() checks can_review_editorial_links(show) per row. */
(function () {
  const DS = window.LawCrimeDesignSystem_e18d68;
  const T = window.titleize || ((s) => String(s || "").replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()));
  const icons = () => setTimeout(() => window.lucide && window.lucide.createIcons(), 30);
  const Ic = ({ n }) => <span className="ic-wrap" key={n}><i data-lucide={n} /></span>;
  const API = () => window.LedgerAPI.ed;
  const PAGE = 30; // episode cards per page

  const GRAIN_ICON = { request: "clipboard-list", folder: "folder", asset: "film" };
  const RULE = {
    received_request: "Received request on the matter",
    case_folder: "The matter's case folder",
    iconik_asset: "Iconik asset on the matter",
  };
  const REQ_TYPE = { foia: "FOIA", court_provided: "Court", purchased: "Purchased", licensed: "Licensed" };
  const fmtDate = (d) => (d ? window.LedgerTime.fmtDate(d) : "Undated");
  const fmtWhen = (iso) => (iso ? new Date(iso).toLocaleString("en-US",
    { month: "short", day: "numeric", hour: "numeric", minute: "2-digit", timeZone: "America/New_York" }) : "");
  const who = (email) => (email || "").split("@")[0] || "—";

  function Conf({ value }) {
    if (value == null) return null;
    const v = Number(value);
    return <span className={"tr-conf tr-conf--" + (v >= 0.75 ? "mid" : "low")} title="How strong the guess is: a received request is the strongest, an Iconik asset the weakest">{v.toFixed(2)}</span>;
  }

  /** What the row is, in one or two lines. */
  function MaterialText({ r }) {
    const label = API().materialLabel(r);
    if (r.grain === "request") {
      const kinds = (r.content_kind || []).map(T).join(", ");
      return (
        <>
          <b>{label}</b>{" "}
          <span className="ed-muted">{[REQ_TYPE[r.request_type] || (r.request_type && T(r.request_type)), kinds, r.request_status && T(r.request_status),
            r.request_created_at && `requested ${fmtDate(r.request_created_at.slice(0, 10))}`].filter(Boolean).join(" · ")}</span>
          {r.request_details && <div className="ed-clip">{r.request_details}</div>}
        </>
      );
    }
    return (
      <>
        <b>{label}</b>{" "}
        <span className="ed-muted">{r.grain === "folder" ? (r.storage_system ? r.storage_system.toUpperCase() + " folder" : "Folder") : T(r.content_type || "asset")}</span>
        {r.asset_url && <div className="ed-muted ed-mat-path">{r.asset_url}</div>}
      </>
    );
  }

  function Row({ r, onDone, onError }) {
    const { Button, Input } = DS;
    const [rejecting, setRejecting] = React.useState(false);
    const [why, setWhy] = React.useState("");
    const [busy, setBusy] = React.useState(false);
    const ev = r.evidence || {};
    async function decide(d) {
      setBusy(true);
      try {
        await API().decideUsage(r.usage_id, d, why || null);
        onDone([r.usage_id], `${d === "confirm" ? "Confirmed" : "Rejected"}: ${API().materialLabel(r)} in ${r.piece_title}`);
      } catch (x) { onError(x.message || String(x)); setBusy(false); }
    }
    return (
      <div className="ed-lr-row">
        <span className="ed-mat-ic"><Ic n={GRAIN_ICON[r.grain] || "file"} /></span>
        <div className="ed-lr-body">
          <div className="ed-lr-top">
            <span className="ed-mat-what"><MaterialText r={r} /></span>
            <span className="ed-lr-meta"><Conf value={r.confidence} /></span>
          </div>
          <div className="ed-lr-ev ed-muted">
            {RULE[ev.rule] || "Proposed from the matter"}
            {ev.undated ? ". The episode has no air date, so timing could not be checked" : ""}.
          </div>
          <div className="ed-lr-actions">
            {rejecting ? (
              <>
                <Input value={why} onChange={(x) => setWhy(x.target.value)} placeholder="Why? (optional, kept on the record)" />
                <button type="button" className="link-btn" onClick={() => setRejecting(false)} disabled={busy}>Cancel</button>
                <Button size="sm" variant="secondary" icon="x" onClick={() => decide("reject")} disabled={busy}>{busy ? "…" : "Confirm: not used"}</Button>
              </>
            ) : (
              <>
                <button type="button" className="link-btn" onClick={() => setRejecting(true)} disabled={busy}>Not used</button>
                <Button size="sm" variant="primary" icon="check" onClick={() => decide("confirm")} disabled={busy}>{busy ? "Saving…" : "Used"}</Button>
              </>
            )}
          </div>
        </div>
      </div>
    );
  }

  // One card per episode: everything proposed for it, judged together.
  function EpisodeCard({ g, onDone, onError, onOpenMatter }) {
    const { Button } = DS;
    const [armed, setArmed] = React.useState(null);
    const [busy, setBusy] = React.useState(false);
    async function bulk(decision) {
      setBusy(true);
      try {
        const res = await API().decideUsages(g.rows.map((r) => ({ id: r.usage_id, decision })));
        const ok = res.filter((x) => x.ok).map((x) => x.id);
        const bad = res.filter((x) => !x.ok);
        onDone(ok, `${ok.length} ${decision === "confirm" ? "marked used" : "marked not used"} in ${g.title}.`);
        if (bad.length) onError(`${bad.length} not saved: ` + Array.from(new Set(bad.map((x) => x.error))).slice(0, 3).join("; "));
      } catch (e) { onError(e.message || String(e)); }
      setBusy(false); setArmed(null);
    }
    const matters = Array.from(new Map(g.rows.map((r) => [r.matter_id, r])).values());
    return (
      <div className="ed-card ed-lr-card">
        <div className="ed-lr-mhead">
          <a className="ed-lr-mlink" href={`#ed/ep/${g.display_id}`} title="Open the episode">{g.title}</a>
          <span className="ed-muted">{[fmtDate(g.air_date), g.display_id].join(" · ")}</span>
          <span style={{ flex: 1 }} />
          {g.rows.length > 1 && (
            <span className="ed-lr-gact">
              {armed ? (
                <>
                  <button type="button" className="link-btn" onClick={() => setArmed(null)} disabled={busy}>Cancel</button>
                  <Button size="sm" variant={armed === "confirm" ? "primary" : "secondary"} icon={armed === "confirm" ? "check-check" : "x"}
                          onClick={() => bulk(armed)} disabled={busy}>
                    {busy ? "Saving…" : `Yes, all ${g.rows.length} ${armed === "confirm" ? "used" : "not used"}`}
                  </Button>
                </>
              ) : (
                <>
                  <button type="button" className="link-btn" onClick={() => setArmed("reject")}>None used</button>
                  <Button size="sm" variant="secondary" icon="check-check" onClick={() => setArmed("confirm")}>All {g.rows.length} used</Button>
                </>
              )}
            </span>
          )}
        </div>
        <div className="ed-muted ed-mat-matters">
          From {matters.map((m, i) => (
            <React.Fragment key={m.matter_id || i}>
              {i > 0 && ", "}
              <a href="#" className="ed-lab" onClick={(e) => { e.preventDefault(); onOpenMatter && onOpenMatter(m.matter_id); }}>{m.matter_label || m.matter_display_id}</a>
            </React.Fragment>
          ))}
        </div>
        {g.rows.map((r) => <Row key={r.usage_id} r={r} onDone={onDone} onError={onError} />)}
      </div>
    );
  }

  function Decided({ rows }) {
    if (!rows.length) {
      return <div className="ledger-empty"><Ic n="inbox" /><b>No decisions yet</b><span>Material marked used or not used in the last 7 days shows here.</span></div>;
    }
    return (
      <div className="ed-tablewrap">
        <table className="ed-table">
          <thead><tr><th style={{ width: 130 }}>When</th><th style={{ width: 110 }}>Who</th><th>Episode</th><th style={{ width: "34%" }}>Material</th><th style={{ width: 100 }}>Decision</th></tr></thead>
          <tbody>
            {rows.map((r) => (
              <tr key={r.usage_id}>
                <td className="ed-muted">{fmtWhen(r.decided_at)}</td>
                <td>{who(r.decided_email)}</td>
                <td><a href={`#ed/ep/${r.piece_display_id}`} className="ed-lab">{r.piece_title}</a>
                  <div className="ed-muted">{r.show_label} · {fmtDate(r.air_date)}</div></td>
                <td>{API().materialLabel(r)}<div className="ed-muted">{r.matter_label}</div>
                  {r.notes && <div className="ed-muted">“{r.notes}”</div>}</td>
                <td><span className={"ed-pill " + (r.status === "confirmed" ? "ok" : "n")}>{r.status === "confirmed" ? "Used" : "Not used"}</span></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
  }

  function Material({ scope, shows, onOpenMatter, onCountsChanged }) {
    const { Button } = DS;
    const [view, setView] = React.useState("pending");
    const [rows, setRows] = React.useState(null);
    const [decided, setDecided] = React.useState(null);
    const [counts, setCounts] = React.useState(null);
    const [show, setShow] = React.useState("");
    const [q, setQ] = React.useState("");
    const [shown, setShown] = React.useState(PAGE);
    const [err, setErr] = React.useState(null);
    const [msg, setMsg] = React.useState(null);

    const refreshCounts = React.useCallback(() => {
      API().usageCounts().then((c) => { setCounts(c); onCountsChanged && onCountsChanged(c); }).catch(() => {});
    }, [onCountsChanged]);
    React.useEffect(() => {
      if (!scope) return;
      API().usageQueue({ status: "pending", scope }).then(setRows).catch((e) => setErr(e.message || String(e)));
      refreshCounts();
    }, [scope, refreshCounts]);
    React.useEffect(() => {
      if (view !== "decided" || !scope) return;
      setDecided(null);
      API().usageQueue({ status: "decided", scope }).then(setDecided).catch((e) => setErr(e.message || String(e)));
    }, [view, scope]);
    React.useEffect(() => { icons(); });
    React.useEffect(() => { setShown(PAGE); }, [show, q]);

    const label = Object.fromEntries((shows || []).map((s) => [s.show_key, s.label]));
    const filtered = React.useMemo(() => {
      const needle = q.trim().toLowerCase();
      return (rows || []).filter((r) => (!show || r.show_key === show) &&
        (!needle || [r.piece_title, r.piece_display_id, r.matter_label, r.matter_display_id, r.request_display_id, r.asset_title, r.asset_url]
          .join(" ").toLowerCase().includes(needle)));
    }, [rows, show, q]);
    const sections = React.useMemo(() => {
      const byShow = new Map();
      filtered.forEach((r) => {
        let s = byShow.get(r.show_key);
        if (!s) { s = { show_key: r.show_key, label: r.show_label, groups: new Map(), n: 0 }; byShow.set(r.show_key, s); }
        let g = s.groups.get(r.piece_id);
        if (!g) { g = { piece_id: r.piece_id, title: r.piece_title, display_id: r.piece_display_id, air_date: r.air_date, rows: [] }; s.groups.set(r.piece_id, g); }
        g.rows.push(r); s.n++;
      });
      // Requests first inside an episode (the strongest guesses), then folders, then assets.
      const order = { request: 0, folder: 1, asset: 2 };
      return Array.from(byShow.values()).sort((a, b) => b.n - a.n).map((s) => ({
        ...s,
        groups: Array.from(s.groups.values())
          .map((g) => ({ ...g, rows: g.rows.sort((a, b) => (order[a.grain] - order[b.grain]) || (Number(b.confidence) - Number(a.confidence))) }))
          .sort((a, b) => String(b.air_date || "").localeCompare(String(a.air_date || ""))),
      }));
    }, [filtered]);
    let budget = shown;
    const paged = sections.map((s) => { const g = s.groups.slice(0, Math.max(0, budget)); budget -= g.length; return { ...s, groups: g }; })
      .filter((s) => s.groups.length);
    const totalCards = sections.reduce((a, s) => a + s.groups.length, 0);

    function done(ids, how) {
      const drop = new Set(ids);
      setRows((rs) => (rs || []).filter((r) => !drop.has(r.usage_id)));
      setMsg(how); setErr(null);
      refreshCounts();
    }
    const showKeys = Object.keys((counts && counts.by_show) || {}).sort((a, b) => counts.by_show[b] - counts.by_show[a]);

    return (
      <div>
        <p className="ed-lede">
          Material on a confirmed episode's matter that the episode may have used: received requests, the case folder and
          Iconik assets. Mark what it actually used. That answers “has this been used, and where?” on the request page.
        </p>
        <div className="ed-filters">
          <button type="button" className={"ed-chip" + (view === "pending" ? " on" : "")} onClick={() => setView("pending")}>
            Needs a decision {counts && <b>{counts.pending}</b>}</button>
          <button type="button" className={"ed-chip" + (view === "decided" ? " on" : "")} onClick={() => setView("decided")}>Decided</button>
        </div>
        {err && <div className="login__err" style={{ marginBottom: 12 }}>{err}</div>}
        {msg && <div className="ledger-ok" style={{ marginBottom: 12 }}>{msg}</div>}

        {view === "decided" ? (decided === null ? <div className="ed-muted">Loading…</div> : <Decided rows={decided} />) : (
          <>
            <div className="ed-filters">
              {showKeys.length > 1 && (
                <>
                  <button type="button" className={"ed-chip" + (!show ? " on" : "")} onClick={() => setShow("")}>All shows</button>
                  {showKeys.map((k) => (
                    <button key={k} type="button" className={"ed-chip" + (show === k ? " on" : "")} onClick={() => setShow(show === k ? "" : k)}>
                      {label[k] || T(k)} <b>{counts.by_show[k]}</b>
                    </button>
                  ))}
                </>
              )}
              <input className="ed-input ed-lr-find" value={q} onChange={(e) => setQ(e.target.value)} placeholder="Find an episode, matter or request…" />
              {rows && <span className="ed-muted">{filtered.length.toLocaleString()} item{filtered.length === 1 ? "" : "s"} · {totalCards.toLocaleString()} episode{totalCards === 1 ? "" : "s"}</span>}
            </div>
            {rows === null && !err && <div className="ed-muted">Loading…</div>}
            {rows && sections.length === 0 && (
              <div className="ledger-empty"><Ic n="check-check" /><b>Nothing waiting here</b>
                <span>Material is proposed when an episode's matter link is confirmed, and nightly as new material arrives.</span></div>
            )}
            {paged.map((s) => (
              <div key={s.show_key} className="ed-lr-show">
                {(!show && sections.length > 1) && <h3 className="ed-sec">{s.label} <span className="ed-muted">{s.n} item{s.n === 1 ? "" : "s"}</span></h3>}
                {s.groups.map((g) => <EpisodeCard key={g.piece_id} g={g} onDone={done} onError={setErr} onOpenMatter={onOpenMatter} />)}
              </div>
            ))}
            {totalCards > shown && (
              <div style={{ textAlign: "center", margin: "14px 0" }}>
                <Button size="sm" variant="secondary" onClick={() => setShown((n) => n + PAGE)}>Show more ({(totalCards - shown).toLocaleString()} episodes left)</Button>
              </div>
            )}
          </>
        )}
      </div>
    );
  }

  window.LedgerEditorialMaterial = Material;
})();
