/* TagFilter — the tag taxonomy as ONE collapsed multi-select control.
   Assigns window.LedgerTagFilter (and window.LedgerTagBrowse for the old grid,
   which is now used only where a full index is genuinely wanted).

   WHAT THIS REPLACES AND WHY. Until 2026-09-11 the Search page rendered the tag
   taxonomy FOUR times over. The zero-query state dumped all 49 tags in 6 category
   groups as a wall of chips; then, the moment you typed, the matching tags
   appeared again as a chip rail above the results, again as a count on the facet
   tabs, and again as the full filtered grid under a "Tags" facet. Clicking any one
   of them LEFT the page for a Matters list filtered to exactly one tag, because
   `listMatters` took `tag`, singular. Operator, 2026-09-11: "particularly awful
   user experience… it'd also be nice if they could select multiple tags."

   So: one control, collapsed by default, that holds a SET of tags and stays out of
   the way until asked for. Selected tags live outside it as removable chips, so
   what is filtering your results is visible without opening anything. The taxonomy
   is 49 tags in 6 categories across 6,498 tagged matters — small enough to browse
   inside the panel, far too much to be the page. */
(function () {
  const T = window.titleize;
  const icons = () => setTimeout(() => window.lucide && window.lucide.createIcons(), 30);

  // 🐛 INLINE SVG, NOT LUCIDE, FOR THE TWO ICONS THAT CHANGE WITH STATE — and here
  // that is not a polish issue, it CRASHED THE PAGE. lucide's createIcons() REPLACES
  // an `<i data-lucide>` node with an `<svg>`. React still believes the `<i>` is
  // there, so when the checkmark is conditionally removed (deselecting a tag) React
  // calls removeChild on a node that is no longer a child and throws
  // `NotFoundError`, which unmounts the whole Search tree — a blank page from
  // clicking a tag on and then off. Caught in a browser; nothing static would show
  // it. The caret has the milder form of the same bug: its `data-lucide` attribute
  // changes but the node React patches is detached, so it silently never flips.
  //
  // ⚠️ THIS IS A CLASS, NOT TWO BUGS. The rule for this codebase: an icon that is
  // conditionally rendered, or whose name depends on state, must be inline SVG.
  // lucide is fine for the fixed ones. Third occurrence in as many days (the sort
  // chevron was the second); registered in known-issues.md with the shared helper
  // these three want.
  const GLYPH = {
    check: ["M20 6 9 17l-5-5"],
    chevronUp: ["m18 15-6-6-6 6"],
    chevronDown: ["m6 9 6 6 6-6"],
  };
  function Glyph({ name, size, className }) {
    return (
      <svg className={className} viewBox="0 0 24 24" width={size || 14} height={size || 14}
           fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"
           strokeLinejoin="round" aria-hidden="true">
        {GLYPH[name].map((d, i) => <path key={i} d={d} />)}
      </svg>
    );
  }

  // Stable category order (matches how the taxonomy is grouped in AI Config).
  const CAT_ORDER = ["crime", "relationship", "people", "legal", "context", "evidence"];
  const catRank = (c) => { const i = CAT_ORDER.indexOf(c || ""); return i < 0 ? 99 : i; };

  // Loads the taxonomy once and hands back only the tags actually in use. A tag no
  // matter carries is not a filter, it is a dead end with a zero next to it.
  function useTaxonomy() {
    const [tags, setTags] = React.useState(null);
    React.useEffect(() => {
      let live = true;
      Promise.all([window.LedgerAPI.listTags(), window.LedgerAPI.tagCounts()])
        .then(([ts, cs]) => {
          if (!live) return;
          setTags((ts || [])
            .map((t) => ({ ...t, count: cs[t.tag_key] || 0 }))
            .filter((t) => t.count > 0));
        })
        .catch(() => { if (live) setTags([]); });
      return () => { live = false; };
    }, []);
    return tags;
  }

  // selected  — array of tag_key
  // onChange(nextArray)
  // mode / onMode — "all" (carries every tag) | "any" (carries at least one)
  function TagFilter({ tags, selected, onChange, mode, onMode }) {
    const [open, setOpen] = React.useState(false);
    const [needle, setNeedle] = React.useState("");
    const box = React.useRef(null);
    const sel = selected || [];

    React.useEffect(() => {
      function onDoc(e) { if (box.current && !box.current.contains(e.target)) setOpen(false); }
      function onEsc(e) { if (e.key === "Escape") setOpen(false); }
      document.addEventListener("mousedown", onDoc);
      document.addEventListener("keydown", onEsc);
      return () => { document.removeEventListener("mousedown", onDoc); document.removeEventListener("keydown", onEsc); };
    }, []);
    React.useEffect(() => { icons(); }, [open, needle, tags, sel.length, mode]);

    const byKey = React.useMemo(() => {
      const m = {}; (tags || []).forEach((t) => { m[t.tag_key] = t; }); return m;
    }, [tags]);

    const n = (needle || "").trim().toLowerCase();
    const shown = (tags || []).filter((t) =>
      !n || t.label.toLowerCase().includes(n) || t.tag_key.toLowerCase().includes(n));

    // Group the filtered tags by category, usage desc inside each.
    const groups = {};
    shown.forEach((t) => { (groups[t.category || "other"] = groups[t.category || "other"] || []).push(t); });
    const cats = Object.keys(groups).sort((a, b) => catRank(a) - catRank(b) || a.localeCompare(b));
    cats.forEach((c) => groups[c].sort((a, b) => b.count - a.count || a.label.localeCompare(b.label)));

    const toggle = (key) => onChange(sel.includes(key) ? sel.filter((k) => k !== key) : sel.concat(key));

    return (
      <div className="tagf">
        <div className="tagf__row">
          <div className="tagf__pop" ref={box}>
            <button type="button" className={"tagf__btn" + (sel.length ? " tagf__btn--on" : "")}
              onClick={() => setOpen((o) => !o)} aria-expanded={open}>
              <i data-lucide="tag" />
              <span>{sel.length ? `${sel.length} tag${sel.length === 1 ? "" : "s"}` : "Filter by tag"}</span>
              <Glyph name={open ? "chevronUp" : "chevronDown"} className="tagf__caret" />
            </button>

            {open && (
              <div className="tagf__panel">
                <div className="tagf__find">
                  <i data-lucide="search" />
                  <input autoFocus value={needle} placeholder="Find a tag…"
                    onChange={(e) => setNeedle(e.target.value)} />
                  {needle && <button type="button" className="tagf__x" onClick={() => setNeedle("")}
                    title="Clear"><i data-lucide="x" /></button>}
                </div>
                <div className="tagf__list">
                  {tags === null && <div className="tagf__note">Loading tags…</div>}
                  {tags && shown.length === 0 && <div className="tagf__note">No tag matches “{needle}”.</div>}
                  {cats.map((c) => (
                    <div key={c} className="tagf__grp">
                      <div className="tagf__cat">{T(c)}</div>
                      {groups[c].map((t) => {
                        const on = sel.includes(t.tag_key);
                        return (
                          <button key={t.tag_key} type="button"
                            className={"tagf__opt" + (on ? " tagf__opt--on" : "")}
                            onClick={() => toggle(t.tag_key)} aria-pressed={on}>
                            <span className="tagf__tick">{on ? <Glyph name="check" size={11} /> : null}</span>
                            <span className="tagf__lbl">{t.label}</span>
                            <span className="tagf__ct">{t.count.toLocaleString()}</span>
                          </button>
                        );
                      })}
                    </div>
                  ))}
                </div>
              </div>
            )}
          </div>

          {/* The combinator only exists once it can change an answer. With one tag
              selected "all" and "any" are the same query, and a control whose two
              settings do the same thing teaches people it does nothing. */}
          {sel.length > 1 && (
            <div className="tagf__mode" role="group" aria-label="Combine tags">
              <button type="button" className={mode === "all" ? "on" : ""} onClick={() => onMode("all")}
                title="Matters carrying every selected tag">All of these</button>
              <button type="button" className={mode === "any" ? "on" : ""} onClick={() => onMode("any")}
                title="Matters carrying at least one selected tag">Any of these</button>
            </div>
          )}

          {sel.length > 0 && (
            <button type="button" className="link-btn tagf__clear" onClick={() => onChange([])}>
              <i data-lucide="x" /> Clear tags
            </button>
          )}
        </div>

        {/* Selected tags sit OUTSIDE the panel: what is filtering your results has
            to be visible without opening a control to check. */}
        {sel.length > 0 && (
          <div className="tagf__chips">
            {sel.map((k) => (
              <button key={k} type="button" className="tagf__chip" onClick={() => toggle(k)}
                title="Remove this tag">
                {(byKey[k] && byKey[k].label) || T(k)}
                <i data-lucide="x" />
              </button>
            ))}
          </div>
        )}
      </div>
    );
  }

  // The old full-taxonomy grid. No longer the Search page's body — kept because a
  // deliberate "show me every tag" browse is still a reasonable thing to want, and
  // the quick-chip row on the Search empty state is a trimmed version of it.
  function TagBrowse({ tags, selected, onToggle, limit }) {
    const sel = selected || [];
    const list = limit ? (tags || []).slice(0, limit) : (tags || []);
    React.useEffect(() => { icons(); }, [tags, sel.length]);
    if (!list.length) return null;
    return (
      <div className="tagsidx-grid">
        {list.map((t) => (
          <button key={t.tag_key} type="button"
            className={"tagsidx-tag" + (sel.includes(t.tag_key) ? " tagsidx-tag--on" : "")}
            title={`${t.count} matter${t.count === 1 ? "" : "s"} tagged “${t.label}”`}
            onClick={() => onToggle(t.tag_key)}>
            <span className="tagsidx-lbl"><i data-lucide="tag" />{t.label}</span>
            <span className="tagsidx-ct">{t.count.toLocaleString()}</span>
          </button>
        ))}
      </div>
    );
  }

  window.LedgerTagFilter = TagFilter;
  window.LedgerTagBrowse = TagBrowse;
  window.LedgerTagTaxonomy = useTaxonomy;
})();
