/* Coverage — the Content Center's daily "today's feeds" board plus the shared
   coverage pill. Answers, for editorial and the Content Center alike: what are we
   covering today, how is each trial coming in, and where do we find the feed.
   Assigns window.LedgerCoverage (board), window.LedgerCoveragePill, and
   window.LedgerCoverageUtil (label maps reused by Matters/Calendar/Search). */
(function () {
  const DS = window.LawCrimeDesignSystem_e18d68;
  const T = window.titleize;
  const icons = () => setTimeout(() => window.lucide && window.lucide.createIcons(), 30);

  // Short, board-friendly method labels (the pill + board use these).
  const METHOD_SHORT = {
    pool_feed: "Pool", court_zoom: "Court Zoom", court_webex: "Court WebEx",
    court_other: "Court feed", our_unit: "Our Unit", own_photographer: "Own crew",
  };
  const DELIVERY_LABEL = { live_feed: "Live feed", post_upload: "Uploads after" };
  const methodShort = (m) => (m ? (METHOD_SHORT[m] || T(m)) : "");
  const deliveryLabel = (d) => (d ? (DELIVERY_LABEL[d] || T(d)) : "");

  // Coverage-decision pill colors (matter-level coverage_status, migration 0077).
  // The four legacy cc_tracking_status keys are kept as aliases so a row that has
  // not been repointed yet still renders in the right colour rather than grey.
  const PILL_STYLE = {
    assigned:       { bg: "#e7f6ec", fg: "#137333", bd: "#bfe3c9" },
    // Tracking: blue, not the old yellow (operator rule: no yellow text in these UIs).
    monitoring:     { bg: "#e8f0fe", fg: "#1a56b8", bd: "#c6d8f7" },
    identified:     { bg: "#eef1f5", fg: "#5b6675", bd: "#d9dee6" },
    adjudicated:    { bg: "#f2f0eb", fg: "#6b6b6b", bd: "#e0ddd4" },
    covering:       { bg: "#e7f6ec", fg: "#137333", bd: "#bfe3c9" },  // legacy
    tracking:       { bg: "#e8f0fe", fg: "#1a56b8", bd: "#c6d8f7" },  // legacy
    needs_research: { bg: "#eef1f5", fg: "#5b6675", bd: "#d9dee6" },  // legacy
  };

  // The coverage pill. `status` = matters.coverage_status; `method` = the
  // matter-default (or resolved) ingest_method. Renders nothing when untracked —
  // NULL means Airtable never carried a status, which is not `identified`.
  function CoveragePill({ status, method, small }) {
    if (!status) return null;
    const s = PILL_STYLE[status] || PILL_STYLE.identified;
    // `assigned` is the rung where a crew/production is on it, so the ingest
    // method is the useful thing to show next to it.
    const label = (status === "assigned" || status === "covering")
      ? window.vocabLabel("coverage_status", status) + (method ? " · " + methodShort(method) : "")
      : window.vocabLabel("coverage_status", status);
    return (
      <span style={{
        display: "inline-flex", alignItems: "center", whiteSpace: "nowrap",
        borderRadius: 999, fontWeight: 600, lineHeight: 1.2,
        fontSize: small ? 11 : 12, padding: small ? "1px 8px" : "2px 10px",
        background: s.bg, color: s.fg, border: `1px solid ${s.bd}`,
      }}>{label}</span>
    );
  }

  // Small neutral chip for delivery / override markers.
  function MiniChip({ children, tone }) {
    const t = tone === "override"
      ? { bg: "#fdeef0", fg: "#b0243a", bd: "#f3c9d1" }
      : { bg: "var(--lc-paper)", fg: "var(--text-muted)", bd: "var(--border-hair)" };
    return (
      <span style={{
        display: "inline-flex", alignItems: "center", borderRadius: 6, fontSize: 11,
        padding: "1px 7px", background: t.bg, color: t.fg, border: `1px solid ${t.bd}`,
      }}>{children}</span>
    );
  }

  // ---- date helpers (local ISO, no UTC drift) -------------------------------
  const pad = (n) => String(n).padStart(2, "0");
  const iso = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
  const parseISO = (s) => { const [y, m, d] = String(s).split("-").map(Number); return new Date(y, m - 1, d); };
  const addDays = (s, n) => { const d = parseISO(s); d.setDate(d.getDate() + n); return iso(d); };
  const prettyDay = (s) => parseISO(s).toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric" });
  const shortDay = (s) => parseISO(s).toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
  const rangeLabel = (a, b) =>
    `${parseISO(a).toLocaleDateString(undefined, { month: "short", day: "numeric" })} – ${parseISO(b).toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" })}`;

  // `lane` is the chrome scope, passed down from app.jsx. A court event belongs to
  // a matter, so this board inherits the lane like every other matters surface —
  // the switcher is a filter on what appears, not a thing that only some pages obey.
  function CoverageBoard({ lane, onOpenMatter }) {
    const { Card, Select } = DS;
    const [day, setDay] = React.useState(() => iso(new Date()));
    const [span, setSpan] = React.useState("day"); // "day" | "week" — bring up more than one date at a time
    const [rows, setRows] = React.useState(null);
    const [err, setErr] = React.useState(null);
    const [method, setMethod] = React.useState("all");
    const [status, setStatus] = React.useState("all");
    // "Mine" (0159, B3): matters whose Content Center owner is the signed-in user.
    // Offered to Content Ops only, and only once their staff id resolves.
    const [myStaff, setMyStaff] = React.useState(null);
    const [mineOnly, setMineOnly] = React.useState(false);
    React.useEffect(() => {
      const caps = window.LedgerCaps || {};
      if (caps.isContentOps || caps.isAdmin) window.LedgerAPI.myStaffIdAnyDomain().then(setMyStaff).catch(() => {});
    }, []);
    const isMine = (r) => !!(myStaff && r.matters && r.matters.cc_assignee_staff_id === myStaff);
    const isWeek = span === "week";
    const start = day;
    const end = isWeek ? addDays(day, 6) : day;

    const load = React.useCallback(() => {
      setRows(null); setErr(null);
      // NOT PAGED YET, deliberately: `method` and `status` below still filter in the
      // browser over the fetched window, so a page here would filter within a page —
      // the exact defect 0092 removed from the Ops queue. Those two move server-side
      // first; until then this asks for the whole window and says so.
      // Covering only (`assigned`): Lacey 2026-10-15, Tracking does not mean we
      // are covering it, so a Tracking matter's court date is not a feed to set up.
      window.LedgerAPI.coverageBoard({ start, end, lane, coverageStatuses: ["assigned"] })
        .then((r) => setRows(r.rows)).catch((e) => setErr(e.message || String(e)));
    }, [start, end, lane]);
    React.useEffect(() => { load(); }, [load]);
    React.useEffect(() => { icons(); }, [rows, method, status, span]);

    const methodOpts = [{ value: "all", label: "All methods" }]
      .concat(window.LEDGER_VOCAB.coverage_ingest_method.map((m) => ({ value: m, label: methodShort(m) })));
    const statusOpts = [{ value: "all", label: "All statuses" }]
      .concat(window.LEDGER_VOCAB.coverage_setup_status.map((s) => ({ value: s, label: T(s) })));

    // Eastern time per row, then ordered by it within each day: the server's order
    // is by the court-local clock, which interleaves zones wrongly.
    const LT = window.LedgerTime;
    const shown = (rows || [])
      .map((r) => ({ ...r, et: LT.toEastern(r.event_date, r.event_time, (r.matters || {}).crime_location_state) }))
      .filter((r) =>
        (method === "all" || r.ingest_method === method) &&
        (status === "all" || r.setup_status === status) &&
        (!mineOnly || isMine(r)))
      .sort((a, b) => (a.event_date < b.event_date ? -1 : a.event_date > b.event_date ? 1 : 0)
        || ((a.et && a.et.known ? a.et.minutes : 1e9) - (b.et && b.et.known ? b.et.minutes : 1e9)));

    const covered = (rows || []).filter((r) => r.ingest_method).length;
    const unset = (rows || []).filter((r) => !r.ingest_method).length;

    const courtLine = (e) => [e.courthouse, e.courtroom, e.address].filter(Boolean).join(" · ");

    const renderRow = (e) => {
      const m = e.matters || {};
      return (
        // .tbl rows already draw as clickable (pointer + hover), so a row that did
        // nothing read as broken (Lacey 2026-10-07). It now opens the matter.
        <tr key={e.event_id}
            title={onOpenMatter && e.matter_id ? "Open the matter" : undefined}
            onClick={onOpenMatter && e.matter_id ? () => onOpenMatter(e.matter_id) : undefined}>
          {/* event_time is COURT-LOCAL; shown in Eastern with the court's own
              time beneath when the zones differ (window.LedgerTime). */}
          <td className="cdef" style={{ whiteSpace: "nowrap", fontVariantNumeric: "tabular-nums" }}
              title={e.et ? [e.et.courtLabel ? "Court time " + e.et.courtLabel : null, e.et.note].filter(Boolean).join(" · ") || undefined : undefined}>
            {e.et ? (e.et.known ? e.et.short : e.et.short + " local") : "—"}
            {e.et && e.et.courtLabel && <span className="row-sub" style={{ marginLeft: 0 }}>{e.et.courtLabel} court</span>}
          </td>
          <td className="cname">
            {m.display_alias || m.working_title || "Untitled"}
            <span className="row-sub" style={{ display: "flex", gap: 6, alignItems: "center", marginTop: 3 }}>
              <CoveragePill status={m.coverage_status} method={e.ingest_method} small />
              {m.display_id ? <span>{m.display_id}</span> : null}
            </span>
          </td>
          <td className="cdef">
            {T(e.event_type)}
            <span style={{ display: "inline-flex", gap: 4, marginLeft: 6 }}>
              {e.defendant_present === true && <span className="ic-wrap" title="Defendant present"><i data-lucide="user-check" style={{ width: 14, height: 14, color: "var(--lc-slate-500)" }} /></span>}
              {e.camera_allowed === true && <span className="ic-wrap" title="Cameras allowed"><i data-lucide="video" style={{ width: 14, height: 14, color: "var(--lc-slate-500)" }} /></span>}
            </span>
          </td>
          <td className="cdef">
            {e.judge_name ? <div>{e.judge_name}</div> : null}
            <span className="row-sub">{courtLine(e) || "—"}</span>
          </td>
          <td className="cdef">
            {e.ingest_method
              ? <span style={{ display: "inline-flex", gap: 6, alignItems: "center", flexWrap: "wrap" }}>
                  <b style={{ color: "var(--text)" }}>{methodShort(e.ingest_method)}</b>
                  {e.delivery_mode && <MiniChip>{deliveryLabel(e.delivery_mode)}</MiniChip>}
                  {e.coverage_source === "event" && <MiniChip tone="override">override</MiniChip>}
                </span>
              : <span style={{ color: "var(--lc-slate-300)" }}>not set</span>}
          </td>
          <td className="cdef">{e.feed_location || "—"}</td>
          <td className="cdef">
            {e.photographer_name || e.field_package_name
              ? <span>{e.photographer_name || ""}{e.photographer_name && e.field_package_name ? " · " : ""}{e.field_package_name ? <span className="row-sub">{e.field_package_name}</span> : ""}</span>
              : "—"}
          </td>
          <td className="cdef">{e.setup_status ? T(e.setup_status) : "—"}</td>
        </tr>
      );
    };

    const boardTable = (list) => (
      <Card><Card.Body style={{ padding: 0 }}>
        <table className="tbl">
          <thead><tr>
            <th style={{ width: 96 }}>Time ({window.LedgerTime.homeAbbr()})</th>
            <th>Matter</th>
            <th>Event</th>
            <th>Court</th>
            <th>How it comes in</th>
            <th>Feed / where</th>
            <th>Photographer / kit</th>
            <th>Status</th>
          </tr></thead>
          <tbody>{list.map(renderRow)}</tbody>
        </table>
      </Card.Body></Card>
    );

    // In week mode, split the shown events into per-day groups (only days that
    // have events), each under its own date heading.
    const dayGroups = isWeek
      ? Array.from({ length: 7 }, (_, i) => addDays(start, i))
          .map((d) => ({ d, list: shown.filter((e) => e.event_date === d) }))
          .filter((g) => g.list.length)
      : [];

    return (
      <div>
        <div className="page-head">
          <div>
            <div className="eyebrow">Content Center · Daily feeds</div>
            <h1 className="page-title">Coverage<window.LedgerComment section="Coverage board" /></h1>
          </div>
        </div>

        <div className="cal-bar" style={{ marginBottom: 14 }}>
          <div className="cal-nav">
            <button className="cal-navbtn" onClick={() => setDay((d) => addDays(d, isWeek ? -7 : -1))} title={isWeek ? "Previous week" : "Previous day"}><i data-lucide="chevron-left" /></button>
            <button className="cal-navbtn" onClick={() => setDay((d) => addDays(d, isWeek ? 7 : 1))} title={isWeek ? "Next week" : "Next day"}><i data-lucide="chevron-right" /></button>
            <button className="cal-today" onClick={() => setDay(iso(new Date()))}>Today</button>
            <h2 className="cal-month">{isWeek ? rangeLabel(start, end) : prettyDay(day)}</h2>
          </div>
          <div className="cal-bar-right" style={{ display: "flex", gap: 8, alignItems: "center" }}>
            <div className="cal-viewtoggle" role="tablist">
              <button type="button" className={"cal-viewbtn" + (!isWeek ? " is-on" : "")} onClick={() => setSpan("day")}>Day</button>
              <button type="button" className={"cal-viewbtn" + (isWeek ? " is-on" : "")} onClick={() => setSpan("week")}>Week</button>
            </div>
            <input type="date" className="lc-input" style={{ width: 150 }} value={day} onChange={(e) => e.target.value && setDay(e.target.value)} />
            <div style={{ width: 150 }}><Select value={method} onChange={(e) => setMethod(e.target.value)} options={methodOpts} /></div>
            <div style={{ width: 150 }}><Select value={status} onChange={(e) => setStatus(e.target.value)} options={statusOpts} /></div>
            {myStaff && (
              <label className="cal-keyonly">
                <input type="checkbox" checked={mineOnly} onChange={(e) => setMineOnly(e.target.checked)} />
                Mine
              </label>
            )}
          </div>
        </div>

        <div style={{ display: "flex", gap: 16, alignItems: "center", fontSize: 12.5, color: "var(--text-muted)", marginBottom: 12 }}>
          <span><b style={{ color: "var(--text)" }}>{(rows || []).length}</b> court event{(rows || []).length === 1 ? "" : "s"} {isWeek ? "this week" : "today"} for matters marked Covering</span>
          <span><b style={{ color: "var(--text)" }}>{covered}</b> with a coverage setup</span>
          {unset > 0 && <span><b style={{ color: "var(--text)" }}>{unset}</b> not set</span>}
          <span style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 6 }}>
            <MiniChip tone="override">override</MiniChip> = per-event, overrides the matter default
          </span>
        </div>

        {err && <div className="login__err" style={{ marginBottom: 14 }}>{err}</div>}
        {rows === null && <div style={{ color: "var(--text-muted)", fontSize: 13 }}>Loading coverage…</div>}
        {rows && shown.length === 0 && (
          <div className="ledger-empty"><i data-lucide="radio-tower" /><b>Nothing {isWeek ? "this week" : "on this day"}</b>
            <span>No court events for Covering matters{method === "all" && status === "all" && !mineOnly ? "" : " match the filters"} for {isWeek ? rangeLabel(start, end) : prettyDay(day)}.</span></div>
        )}

        {shown.length > 0 && !isWeek && boardTable(shown)}

        {shown.length > 0 && isWeek && dayGroups.map((g) => (
          <div key={g.d} className="cov-daygrp">
            <div className="cov-dayhd">{shortDay(g.d)} <span className="cov-dayct">{g.list.length} event{g.list.length === 1 ? "" : "s"}</span></div>
            {boardTable(g.list)}
          </div>
        ))}
      </div>
    );
  }

  window.LedgerCoveragePill = CoveragePill;
  window.LedgerCoverageUtil = { methodShort, deliveryLabel, METHOD_SHORT, DELIVERY_LABEL, MiniChip };
  window.LedgerCoverage = CoverageBoard;
})();
