/* RequestCalendar — the Asset Requests desk in Content Operations (Phase 1).
   Still named Asset Requests (operator, 2026-10-05): the calendar is the UI the
   team manages requests in, and it opens first. The Content Center staff who
   process requests work from a due-date calendar, not a list. Each person opens their own day, works what is due, and pushes a
   request to the next date to check back. Requesters never appear here as
   assignees (only Content Center staff marked Assignable, 0137).

   What is on the page:
     - Calendar | List. List is the old Asset Requests desk (OpsQueue), kept for
       bulk filters, search by status/type/show, and paging.
     - Owner chips: Mine (default when you are assignable), Everyone, Unassigned,
       and each teammate. In Everyone, each person has a color.
     - Overdue strip: open and past due, most recently overdue first.
     - No-due-date tray beside the grid, so undated requests stay visible.
     - One-click re-date on every card: Tomorrow, Next week, +1 month, +6 months,
       or a date. Each push is logged (asset_request_due_events, 0138), and a card
       shows how many times it was chased.
   Click a card for the existing request page. There is no second editor.

   Dates: only OPEN requests (terminal statuses are done work). Relative re-dates
   count from today in Eastern, not from the old due date: "check back next week"
   means a week from now.
   Assigns window.LedgerRequestCalendar. Needs CalendarKit.jsx, RequestKit.jsx, OpsQueue.jsx. */
(function () {
  const DS = window.LawCrimeDesignSystem_e18d68;
  const T = window.titleize;
  const CK = window.LedgerCalendarKit;
  const K = window.LedgerRequestKit;
  const { iso, parseISO, addDays, addMonths, DOW, shortDate } = CK;
  const icons = () => setTimeout(() => window.lucide && window.lucide.createIcons(), 30);
  // Every icon sits in a span React owns: lucide swaps the <i> for an <svg>, and
  // React unmounting a swapped <i> blanks the app (known issue, ledger-lucide).
  const Ico = ({ n }) => <span className="fcal-ico"><i data-lucide={n} /></span>;

  const MODE_KEY = "ledger.requestCalendar.mode";       // "calendar" | "list"
  const MONTH_CAP = 4;      // cards per month cell before "+N more"
  const STRIP_CAP = 12;     // overdue cards shown before "Open in list"
  const TRAY_CAP = 40;      // undated cards shown before "Open in list"
  // One color per teammate in the Everyone view. Ten distinct hues, readable on
  // paper; a team bigger than this repeats, which the name on the card resolves.
  const PALETTE = ["#2e6bb8", "#c2410c", "#15803d", "#7c3aed", "#b45309", "#0e7490", "#be185d", "#4d7c0f", "#9f1239", "#475569"];

  const readLS = (k, d) => { try { const v = localStorage.getItem(k); return v === null ? d : v; } catch (_) { return d; } };
  const saveLS = (k, v) => { try { localStorage.setItem(k, v); } catch (_) { /* private window */ } };
  const personName = (s) => `${s.first_name || ""} ${s.last_name || ""}`.trim();
  const firstName = (s) => s.first_name || personName(s);

  // ---- Re-date control ------------------------------------------------------
  // The one-click push. Options count from today (Eastern).
  function RedateMenu({ request, onRedate, busy }) {
    // `open` holds the menu's screen position. The menu is position: fixed so a
    // scrolling day cell cannot clip it; any scroll elsewhere closes it.
    const [open, setOpen] = React.useState(null);
    const [custom, setCustom] = React.useState("");
    const ref = React.useRef(null);
    React.useEffect(() => {
      if (!open) return;
      const close = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(null); };
      const onScroll = (e) => { if (!(ref.current && ref.current.contains(e.target))) setOpen(null); };
      document.addEventListener("mousedown", close);
      window.addEventListener("scroll", onScroll, true);
      return () => { document.removeEventListener("mousedown", close); window.removeEventListener("scroll", onScroll, true); };
    }, [open]);
    const toggle = (e) => {
      if (open) { setOpen(null); return; }
      const b = e.currentTarget.getBoundingClientRect();
      const W = 196, H = 230;
      const left = Math.max(8, Math.min(b.right - W, window.innerWidth - W - 8));
      const top = b.bottom + H + 8 > window.innerHeight ? Math.max(8, b.top - H - 2) : b.bottom + 2;
      setOpen({ left, top });
    };
    React.useEffect(() => { if (open) icons(); }, [open]);
    const t = parseISO(CK.todayISO());
    const opts = [
      { label: "Tomorrow", d: addDays(t, 1) },
      { label: "Next week", d: addDays(t, 7) },
      { label: "+1 month", d: addMonths(t, 1) },
      { label: "+6 months", d: addMonths(t, 6) },
    ];
    const pick = (d) => { setOpen(null); setCustom(""); onRedate(request, d); };
    return (
      <span className="fcal-redate" ref={ref} onClick={(e) => e.stopPropagation()}>
        <button type="button" className="fcal-redate-btn" disabled={busy}
          title="Set a new due date" aria-label={"Re-date " + request.display_id}
          onClick={toggle}>
          <Ico n="calendar-clock" />
        </button>
        {open && (
          <div className="fcal-redate-menu" role="menu" style={{ left: open.left, top: open.top }}>
            <div className="fcal-redate-head">Check back</div>
            {opts.map((o) => (
              <button key={o.label} type="button" role="menuitem" onClick={() => pick(iso(o.d))}>
                <b>{o.label}</b><span>{DOW[o.d.getDay()]} {shortDate(iso(o.d))}</span>
              </button>
            ))}
            <form className="fcal-redate-custom" onSubmit={(e) => { e.preventDefault(); if (custom) pick(custom); }}>
              <input type="date" value={custom} onChange={(e) => setCustom(e.target.value)} aria-label="Pick a date" />
              <button type="submit" disabled={!custom}>Set</button>
            </form>
          </div>
        )}
      </span>
    );
  }

  // ---- One request card -----------------------------------------------------
  function ReqCard({ r, color, showOwner, chased, busy, onOpen, onRedate, compact, showDue }) {
    const overdue = K.isOverdue(r);
    return (
      <div className={"fcal-card" + (compact ? " fcal-card--compact" : "") + (busy ? " is-busy" : "")}
        style={color ? { "--who": color } : undefined}
        role="button" tabIndex={0}
        title={[r.display_id, r.matter_title, r.agency_name, r.assignee_name].filter(Boolean).join(" · ")}
        onClick={() => onOpen(r)} onKeyDown={(e) => { if (e.key === "Enter") onOpen(r); }}>
        <div className="fcal-card-top">
          <span className="fcal-id">{r.display_id}</span>
          {r.request_type && !compact && <span className="chip">{T(r.request_type)}</span>}
          {chased > 0 && <span className="fcal-chased" title={`Re-dated ${chased} time${chased === 1 ? "" : "s"} in Ledger`}>↻{chased}</span>}
          <RedateMenu request={r} onRedate={onRedate} busy={busy} />
        </div>
        <div className="fcal-card-title">{r.matter_title || r.slug || "— unlinked —"}</div>
        {!compact && (r.agency_name || showOwner || showDue) && (
          <div className="fcal-card-sub">
            {showDue && r.due_date && <span className={overdue ? "overdue" : ""}>{overdue ? `${Math.abs(K.daysUntil(r.due_date))}d over` : shortDate(r.due_date)}</span>}
            {r.agency_name && <span>{r.agency_name}</span>}
            {showOwner && <span className="fcal-who">{r.assignee_name || "Unassigned"}</span>}
          </div>
        )}
      </div>
    );
  }

  function RequestCalendar({ onOpenMatter }) {
    const [mode, setModeState] = React.useState(() => readLS(MODE_KEY, "calendar") === "list" ? "list" : "calendar");
    const setMode = (m) => { setModeState(m); saveLS(MODE_KEY, m); };
    const [listFilter, setListFilter] = React.useState(null);  // seeds the List view
    const [assignable, setAssignable] = React.useState(null);
    const [me, setMe] = React.useState(undefined);              // undefined = not known yet
    // Not remembered between visits on purpose: the desk always opens on Mine
    // (spec), so nobody lands on a teammate's day without noticing.
    const [owner, setOwner] = React.useState(null);
    const [openReq, setOpenReq] = React.useState(null);

    // Who am I, and who can hold requests. "Mine" is the default when I am one of
    // them; otherwise (admins, Media, leadership) the desk opens on Everyone.
    React.useEffect(() => {
      Promise.all([
        window.LedgerAPI.listAssignableStaff().catch(() => []),
        window.LedgerAPI.myStaffIdAnyDomain().catch(() => null),
      ]).then(([staff, mine]) => {
        setAssignable(staff); setMe(mine);
        const isMeAssignable = mine && staff.some((s) => s.staff_id === mine);
        setOwner(isMeAssignable ? mine : "all");
      });
    }, []);

    const colorOf = React.useMemo(() => {
      const m = {}; (assignable || []).forEach((s, i) => { m[s.staff_id] = PALETTE[i % PALETTE.length]; });
      return m;
    }, [assignable]);

    const openInList = (filter) => { setListFilter({ assignee: owner || "all", ...filter }); setMode("list"); };

    // The request page opens OVER the calendar, which stays mounted (hidden), so
    // closing it lands you on the same week and owner. Coming back reloads the
    // desk: the request may have changed.
    const [reloadKey, setReloadKey] = React.useState(0);
    const detail = openReq && (
      <div className="opsq">
        <K.RequestDetail request={openReq}
          onBack={() => { setOpenReq(null); setReloadKey((n) => n + 1); }}
          onChanged={() => {}}
          onOpenMatter={onOpenMatter} />
      </div>
    );

    const isMeAssignable = me && (assignable || []).some((s) => s.staff_id === me);
    const chips = [];
    if (isMeAssignable) chips.push({ id: me, label: "Mine", icon: "user" });
    chips.push({ id: "all", label: "Everyone", icon: "users" });
    chips.push({ id: "unassigned", label: "Unassigned", icon: "user-x" });
    (assignable || []).filter((s) => s.staff_id !== me).forEach((s) => chips.push({ id: s.staff_id, label: personName(s), color: colorOf[s.staff_id] }));

    return (
      <>
      {detail}
      <div className="fcal" style={openReq ? { display: "none" } : undefined}>
        <div className="page-head">
          <div>
            <div className="eyebrow">Content Operations</div>
            <h1 className="page-title">Asset Requests<window.LedgerComment section="Asset Requests" /></h1>
            <p className="page-sub">Open requests by due date. Work today's, then push each one to the day you will check back.</p>
          </div>
          <div className="cal-viewtoggle" role="tablist" aria-label="Calendar or list">
            <button type="button" className={"cal-viewbtn" + (mode === "calendar" ? " is-on" : "")} onClick={() => setMode("calendar")}>Calendar</button>
            <button type="button" className={"cal-viewbtn" + (mode === "list" ? " is-on" : "")} onClick={() => { setListFilter({ assignee: owner || "all" }); setMode("list"); }}>List</button>
          </div>
        </div>

        {mode === "list" && (
          <window.LedgerOpsQueue embedded onOpenMatter={onOpenMatter}
            key={JSON.stringify(listFilter || {})} initialFilter={listFilter || undefined} />
        )}

        {mode === "calendar" && owner === null && <div style={{ color: "var(--text-muted)", fontSize: 13 }}>Loading…</div>}
        {mode === "calendar" && owner !== null && (
          <>
            <div className="fcal-owners" role="tablist" aria-label="Whose requests">
              {chips.map((c) => (
                <button key={c.id} type="button" className={"fcal-owner" + (owner === c.id ? " is-on" : "")}
                  aria-pressed={owner === c.id} onClick={() => setOwner(c.id)}>
                  {c.color ? <i className="fcal-swatch" style={{ background: c.color }} /> : <Ico n={c.icon} />}
                  {c.label}
                </button>
              ))}
            </div>
            <CalendarDesk owner={owner} colorOf={colorOf} showOwner={owner === "all"}
              onOpen={setOpenReq} openInList={openInList} reloadKey={reloadKey} />
          </>
        )}
      </div>
      </>
    );
  }

  // ---- The calendar, the overdue strip and the undated tray -----------------
  function CalendarDesk({ owner, colorOf, showOwner, onOpen, openInList, reloadKey }) {
    const nav = CK.useCalendarNav({ defaultView: "week", weekendKey: "ledger.requestCalendar.showWeekends",
                                    weekendDefault: true, viewKey: "ledger.requestCalendar.view" });
    const { isDay, isWeek, cells, weeks, dowRow, nCols, colStyle, showWeekends, from, to, today, cursor } = nav;
    const [due, setDue] = React.useState(null);
    const [overdue, setOverdue] = React.useState(null);     // { rows, total }
    const [undated, setUndated] = React.useState(null);     // { rows, total }
    const [counts, setCounts] = React.useState(null);       // { new, open, overdue, no_due }
    const [chased, setChased] = React.useState({});
    const [busyId, setBusyId] = React.useState(null);
    const [err, setErr] = React.useState(null);
    const [notice, setNotice] = React.useState(null);
    const [showAllOverdue, setShowAllOverdue] = React.useState(false);

    const loadGrid = React.useCallback(() => {
      setErr(null);
      return window.LedgerAPI.listRequestsDueBetween({ from, to, assignee: owner })
        .then((rows) => { setDue(rows); return rows; })
        .catch((e) => { setErr(e.message || String(e)); setDue([]); return []; });
    }, [from, to, owner, reloadKey]);
    const loadSides = React.useCallback(() => {
      const q = (due, sort, pageSize) => window.LedgerAPI.listAssetRequestQueue({ due, openOnly: true, assignee: owner, sort, page: 0, pageSize })
        .then((r) => ({ rows: r.rows, total: r.total }));
      return Promise.all([
        q("overdue", { key: "due_date", dir: "desc" }, 50).catch(() => ({ rows: [], total: 0 })),
        q("no_due", { key: "created_at", dir: "desc" }, TRAY_CAP).catch(() => ({ rows: [], total: 0 })),
      ]).then(([o, u]) => { setOverdue(o); setUndated(u); return [o, u]; });
    }, [owner, reloadKey]);

    React.useEffect(() => { setDue(null); loadGrid(); }, [loadGrid]);
    React.useEffect(() => { setOverdue(null); setUndated(null); setShowAllOverdue(false); loadSides(); }, [loadSides]);
    // The person's numbers (or the team's, in Everyone). Re-read after every re-date.
    const loadCounts = React.useCallback(() =>
      window.LedgerAPI.requestOwnerCounts({ assignee: owner }).then(setCounts).catch(() => setCounts(null)), [owner, reloadKey]);
    React.useEffect(() => { setCounts(null); loadCounts(); }, [loadCounts]);
    // "Chased N times", for everything on screen.
    React.useEffect(() => {
      const ids = [...(due || []), ...((overdue && overdue.rows) || []), ...((undated && undated.rows) || [])].map((r) => r.id);
      if (!ids.length) return;
      let live = true;
      window.LedgerAPI.requestDueChangeCounts(ids).then((c) => { if (live) setChased(c); }).catch(() => {});
      return () => { live = false; };
    }, [due, overdue, undated]);
    React.useEffect(() => { icons(); });

    async function redate(r, newDue) {
      setBusyId(r.id); setErr(null);
      try {
        const res = await window.LedgerAPI.redateAssetRequest(r.id, newDue);
        setChased((c) => ({ ...c, [r.id]: res.due_change_count }));
        setNotice(`${r.display_id} moved to ${DOW[parseISO(newDue).getDay()]} ${shortDate(newDue)}.`);
        setTimeout(() => setNotice(null), 4000);
        await Promise.all([loadGrid(), loadSides(), loadCounts()]);
      } catch (e) { setErr(e.message || String(e)); }
      finally { setBusyId(null); }
    }

    // Requests by due date. With weekends hidden, Saturday and Sunday requests sit
    // on Friday and say which day they are really due, so nothing disappears.
    const byDay = React.useMemo(() => {
      const m = {};
      (due || []).forEach((r) => {
        let k = r.due_date;
        if (!showWeekends) {
          const d = parseISO(k), dow = d.getDay();
          if (dow === 6) k = iso(addDays(d, -1)); else if (dow === 0) k = iso(addDays(d, -2));
        }
        (m[k] = m[k] || []).push(r);
      });
      return m;
    }, [due, showWeekends]);

    const card = (r, extra = {}) => (
      <ReqCard key={r.id} r={r} color={showOwner ? (colorOf[r.assignee] || "#94a3b8") : null}
        showOwner={showOwner} chased={chased[r.id] || 0} busy={busyId === r.id}
        onOpen={onOpen} onRedate={redate} {...extra} />
    );
    const weekendTag = (r, dayKey) => r.due_date !== dayKey ? <span className="fcal-wknd">{DOW[parseISO(r.due_date).getDay()]}</span> : null;

    const overdueRows = overdue ? overdue.rows : [];
    const overdueShown = showAllOverdue ? overdueRows : overdueRows.slice(0, STRIP_CAP);
    const dayRows = isDay ? (byDay[iso(cursor)] || []) : [];

    return (
      <div>
        {/* The four numbers that matter to whoever is selected (operator, 2026-10-05).
            Each opens the list on the same slice. Neutral tones: no yellow. */}
        <div className="fcal-tiles">
          {[
            { k: "new", label: "New", hint: "Not started (status New)", filter: { status: "new" } },
            { k: "open", label: "Open", hint: "Every open request", filter: {} },
            { k: "overdue", label: "Overdue", hint: "Open and past due", filter: { due: "overdue" }, bad: true },
            { k: "no_due", label: "No due date", hint: "Open with no due date", filter: { due: "no_due" } },
          ].map((t) => (
            <button key={t.k} type="button" title={t.hint + " · open in list"}
              className={"fcal-tile" + (t.bad && counts && counts[t.k] > 0 ? " fcal-tile--bad" : "")}
              onClick={() => openInList(t.filter)}>
              <span className="fcal-tile-n">{counts ? counts[t.k].toLocaleString() : "—"}</span>
              <span className="fcal-tile-l">{t.label}</span>
            </button>
          ))}
        </div>

        {/* Overdue first: it is the only thing on this page that is a problem. */}
        {overdue && overdue.total > 0 && (
          <div className="fcal-overdue">
            <div className="fcal-overdue-head">
              <span><Ico n="alarm-clock" /> <b>Overdue</b> <span className="fcal-muted">most recent first</span></span>
              <span className="fcal-overdue-acts">
                {overdueRows.length > STRIP_CAP && (
                  <button type="button" className="link-btn" onClick={() => setShowAllOverdue((v) => !v)}>
                    {showAllOverdue ? "Show fewer" : `Show ${overdueRows.length}`}
                  </button>
                )}
                <button type="button" className="link-btn" onClick={() => openInList({ due: "overdue" })}>Open in list <Ico n="arrow-right" /></button>
              </span>
            </div>
            <div className="fcal-strip">{overdueShown.map((r) => card(r, { showDue: true }))}</div>
          </div>
        )}

        <CK.CalendarHeader nav={nav}>
          <label className="cal-keyonly">
            <input type="checkbox" checked={showWeekends} onChange={(e) => nav.setShowWeekends(e.target.checked)} />
            Weekends
          </label>
        </CK.CalendarHeader>
        {err && <div className="login__err" style={{ marginBottom: 12 }}>{err}</div>}
        {notice && <div className="fcal-notice" role="status"><Ico n="check" /> {notice}</div>}

        <div className="fcal-body">
          <div className="fcal-main">
            {due === null && <div style={{ color: "var(--text-muted)", fontSize: 13 }}>Loading requests…</div>}

            {due !== null && isDay && (
              <div className="fcal-dayview">
                {dayRows.length === 0
                  ? <div className="ledger-empty"><Ico n="calendar-check" /><b>Nothing due</b><span>No open requests due on this day.</span></div>
                  : <div className="fcal-daylist">{dayRows.map((r) => card(r))}</div>}
              </div>
            )}

            {due !== null && !isDay && (
              <div className={"cal-cal fcal-grid" + (isWeek ? " fcal-grid--week" : "")}>
                <div className="cal-dow-row" style={colStyle}>{dowRow.map((d) => <div key={d} className="cal-dow">{d}</div>)}</div>
                {weeks.map((week, wi) => (
                  <div key={wi} className="fcal-weekrow" style={colStyle}>
                    {week.slice(0, nCols).map((d) => {
                      const k = iso(d);
                      const rows = byDay[k] || [];
                      const inMonth = isWeek || d.getMonth() === cursor.getMonth();
                      const shown = isWeek ? rows : rows.slice(0, MONTH_CAP);
                      return (
                        <div key={k} className={"fcal-cell" + (inMonth ? "" : " cal-bgcell--out") + (k === today ? " fcal-cell--today" : "")}>
                          <button type="button" className="fcal-daynum" title="Open this day"
                            onClick={() => { nav.goTo(k); nav.setView("day"); }}>
                            {isWeek ? <><span className="cal-dayname">{DOW[d.getDay()]}</span> {d.getDate()}</> : d.getDate()}
                            {rows.length > 0 && <span className="fcal-count">{rows.length}</span>}
                          </button>
                          <div className="fcal-cell-list">
                            {shown.map((r) => (
                              <React.Fragment key={r.id}>
                                {weekendTag(r, k)}
                                {card(r, { compact: !isWeek })}
                              </React.Fragment>
                            ))}
                            {rows.length > shown.length && (
                              <button type="button" className="fcal-more" onClick={() => { nav.goTo(k); nav.setView("day"); }}>
                                +{rows.length - shown.length} more
                              </button>
                            )}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                ))}
              </div>
            )}
          </div>

          {/* Undated requests stay in sight: a request with no date never comes up. */}
          <aside className="fcal-tray">
            <div className="fcal-tray-head">
              <span><Ico n="calendar-off" /> No due date</span>
              <span className="fcal-count">{undated ? undated.total.toLocaleString() : "—"}</span>
            </div>
            {undated && undated.total === 0 && <div className="fcal-muted" style={{ fontSize: 12.5 }}>Every open request has a date.</div>}
            <div className="fcal-tray-list">{undated && undated.rows.map((r) => card(r))}</div>
            {undated && undated.total > undated.rows.length && (
              <button type="button" className="link-btn" onClick={() => openInList({ due: "no_due" })}>
                All {undated.total.toLocaleString()} in list <Ico n="arrow-right" />
              </button>
            )}
          </aside>
        </div>
      </div>
    );
  }

  window.LedgerRequestCalendar = RequestCalendar;
})();
