// ═══════════════════════════════════════════════════════════════════
// ENTITY REGISTRY (per tenant-project)
// The flat registry of live deal objects — actual instances of
// ontology entities resolved from THIS project's artifacts, with type,
// link degree and role. The relationship wiring over the same objects
// renders on the Knowledge Graph; the abstract type catalog lives
// under Platform · Ontology Reference.
//   · live project   → the real Raisable deal objects (RAISABLE.graph())
//   · store-backed   → entities from the store's concrete graph
//   · other projects → placeholder until the KG step has built the graph
// ═══════════════════════════════════════════════════════════════════
(function () {
  const { useState } = React;
  const I = window.INTEL;
  const R = window.RAISABLE;
  const S = window.ONTOLOGY; // the Intelligence Ontology (Compass World Model) — root of subject identity

  const erMono = (s, extra) => <span style={{ fontFamily: "var(--mono)", fontSize: "8.5px", letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--ink-4)", ...extra }}>{s}</span>;

  // shared ontology visual language (window.OKit) — the SAME palette, marks and
  // glyphs the Knowledge Graph and Ontology Reference use, so the registry reads
  // as the flat view of that graph. Declared once in OntologyKit.jsx.
  const OK = window.OKit;
  const { CAT_COLOR, CAT_ORDER } = OK;
  const catMark = (cat) => <OK.CatMark cat={cat} size={8}></OK.CatMark>;

  // ── the registry table — types every object against the ONTOLOGY, clusters
  // the filter rail by subject category and orders rows canonically. Drives
  // any concrete deal graph whose nodes carry { id, of, name|label, kicker|sub }
  // — the live Raisable graph and store-backed graphs (SAXCAP · Lionsgate) alike.
  function EntityTable({ nodes, edges }) {
    const [typeFilter, setTypeFilter] = useState("all");
    const nm = (n) => n.name || n.label || n.id;
    const role = (n) => n.kicker || n.sub || "";
    // degree per instance (how many deal relationships it participates in)
    const deg = {};
    nodes.forEach((n) => { deg[n.id] = 0; });
    edges.forEach((e) => { if (deg[e.from] != null) deg[e.from]++; if (deg[e.to] != null) deg[e.to]++; });
    // resolve each object's type against the ONTOLOGY — never re-declared here.
    // unknown ids (a subject the ontology has dropped/renamed) degrade visibly.
    const meta = (ofId) => (S && S.subjMeta(ofId)) || { id: ofId, glyph: "·", cat: null, def: "" };
    const known = (ofId) => !!(S && S.SUBJECTS.some((s) => s.id === ofId));
    const order = (S && S.SUBJ_ORDER) || [];
    // present subject types, in canonical ontology order (unknown ones last)
    const present = [];
    nodes.forEach((n) => { if (!present.includes(n.of)) present.push(n.of); });
    present.sort((a, b) => {
      const ia = order.indexOf(a), ib = order.indexOf(b);
      return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib);
    });
    const countOf = (t) => nodes.filter((n) => n.of === t).length;
    // group present subjects by category, in CAT_ORDER, for the filter rail
    const byCat = CAT_ORDER.map((cat) => ({ cat, types: present.filter((t) => meta(t).cat === cat) })).filter((g) => g.types.length);
    const orphan = present.filter((t) => !known(t)); // subjects not in the ontology
    // rows in canonical subject order when unfiltered (groups objects by type)
    const sorted = nodes.slice().sort((a, b) => present.indexOf(a.of) - present.indexOf(b.of));
    const filtered = typeFilter === "all" ? sorted : sorted.filter((n) => n.of === typeFilter);

    const chip = (id, lbl, cat, count, glyph) => {
      const on = typeFilter === id;
      return (
        <button key={id} onClick={() => setTypeFilter(id)} style={{ display: "inline-flex", alignItems: "center", gap: 6, border: "1px solid " + (on ? "var(--accent)" : "var(--rule-hard)"), background: on ? "var(--accent-bg)" : "var(--bg)", color: on ? "var(--accent-lo)" : "var(--ink-2)", padding: "5px 11px", borderRadius: "999px", cursor: "pointer", fontFamily: "var(--mono)", fontSize: "9.5px", letterSpacing: "0.06em", textTransform: "uppercase", fontWeight: 600 }}>
          {glyph ? <span style={{ fontSize: 11, color: cat ? CAT_COLOR[cat] : "var(--ink-3)", fontWeight: 400 }}>{glyph}</span> : null}
          {lbl} <span style={{ color: "var(--ink-4)", fontWeight: 400 }}>{count}</span>
        </button>
      );
    };

    return (
      <div>
        {/* filter rail — clustered by ontology subject category */}
        <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap", marginBottom: 12 }}>
          {chip("all", "All objects", null, nodes.length, null)}
          {byCat.map((g) => (
            <React.Fragment key={g.cat}>
              <span style={{ display: "inline-flex", alignItems: "center", gap: 5, paddingLeft: 4 }}>
                {catMark(g.cat, true)}
                {erMono(S.SUBJ_CATS[g.cat].label, { color: "var(--ink-4)" })}
              </span>
              {g.types.map((t) => chip(t, meta(t).id, g.cat, countOf(t), meta(t).glyph))}
            </React.Fragment>
          ))}
          {orphan.map((t) => chip(t, meta(t).id + " ⚠", null, countOf(t), meta(t).glyph))}
        </div>
        <div style={{ border: "1px solid var(--rule-hard)", borderRadius: "var(--r-3)", overflow: "hidden", marginBottom: 8 }}>
          <div style={{ display: "grid", gridTemplateColumns: "128px 1.1fr 178px 64px 1.2fr", gap: 1, background: "var(--rule)" }}>
            {["Object ID", "Name", "Subject", "Links", "Role in the deal"].map((h) => (
              <div key={h} style={{ background: "var(--bg-2)", padding: "8px 12px" }}><window.MonoLabel>{h}</window.MonoLabel></div>
            ))}
            {filtered.map((n) => {
              const m = meta(n.of), ok = known(n.of), col = m.cat ? CAT_COLOR[m.cat] : "var(--ink-3)";
              return (
              <React.Fragment key={n.id}>
                <div style={{ background: "var(--bg)", padding: "7px 12px", fontFamily: "var(--mono)", fontSize: "9.5px", color: "var(--accent-lo)", letterSpacing: "0.02em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }} title={n.id}>{n.id}</div>
                <div style={{ background: "var(--bg)", padding: "7px 12px", fontSize: "var(--fs-xs)", color: "var(--ink)", fontWeight: 500 }}>{nm(n)}</div>
                <div style={{ background: "var(--bg)", padding: "7px 12px", display: "flex", alignItems: "center", gap: 7 }} title={ok ? m.cat + " · " + m.def : "Not a subject in the current ontology"}>
                  <span style={{ fontFamily: "var(--mono)", fontSize: "9px", fontWeight: 600, color: ok ? "var(--ink-2)" : "var(--accent-lo)", border: "1px solid " + (ok ? "var(--rule-hard)" : "var(--accent-bd)"), borderRadius: "var(--r-1)", padding: "1px 6px 1px 5px", whiteSpace: "nowrap", display: "inline-flex", alignItems: "center", gap: 5 }}>
                    <span style={{ color: col, fontSize: 11, fontWeight: 400 }}>{m.glyph}</span>{m.id}{ok ? "" : " ⚠"}
                  </span>
                  {ok && m.cat ? erMono(m.cat, { color: "var(--ink-4)" }) : null}
                </div>
                <div style={{ background: "var(--bg)", padding: "7px 12px", fontFamily: "var(--mono)", fontSize: "11px", fontWeight: 600, color: deg[n.id] ? "var(--ink-2)" : "var(--ink-4)" }}>{deg[n.id]}</div>
                <div style={{ background: "var(--bg)", padding: "7px 12px", fontSize: "var(--fs-xs)", color: "var(--ink-3)", lineHeight: 1.5 }}>{role(n)}</div>
              </React.Fragment>
            ); })}
          </div>
        </div>
        <p style={{ margin: "0 0 14px", fontFamily: "var(--mono)", fontSize: "8.5px", letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-4)" }}>
          {filtered.length} of {nodes.length} deal objects{typeFilter !== "all" ? " · " + meta(typeFilter).id : ""} · {present.length} of {(S ? S.SUBJECTS.length : present.length)} ontology subjects instantiated{orphan.length ? " · " + orphan.length + " off-ontology ⚠" : ""} · links = edges in the offering graph — see Knowledge Graph for the wiring
        </p>
      </div>
    );
  }

  // ── live project: the Raisable deal objects ──
  function RaisableEntityRegistry() {
    const { nodes, edges } = R.graph();
    return <EntityTable nodes={nodes} edges={edges}></EntityTable>;
  }

  // ── page ───────────────────────────────────────────────────────────
  function EntityRegistry({ proj, tenant, go }) {
    if (proj.live && R) {
      const { nodes, edges } = R.graph();
      return (
        <div>
          <window.PageHead eyebrow={tenant.name + " · " + proj.name} title="Entity Registry"
            sub="The live objects in this project's deal — instances of Intelligence-Ontology subjects (the Compass World Model), not the abstract subject catalog. The relationship wiring over these objects renders on the Knowledge Graph."
            right={<div style={{ textAlign: "right" }}>{erMono(nodes.length + " objects · " + edges.length + " links", { color: "var(--accent-lo)" })}</div>}>
          </window.PageHead>
          <RaisableEntityRegistry></RaisableEntityRegistry>
        </div>
      );
    }
    // store-backed concrete graph (e.g. Lionsgate) once the KG step has run
    const store = proj.kbRef && window[proj.kbRef];
    const graphReady = !!(proj.run && proj.run.steps && proj.run.steps.kg === "done");
    if (store && store.GRAPH && graphReady) {
      const g = store.GRAPH;
      return (
        <div>
          <window.PageHead eyebrow={tenant.name + " · " + proj.name} title="Entity Registry"
            sub="The objects resolved from this project's parsed artifacts — each typed against the Intelligence-Ontology subjects (the Compass World Model). The relationship wiring over these objects renders on the Knowledge Graph."
            right={<div style={{ textAlign: "right" }}>{erMono(g.nodes.length + " objects · " + g.edges.length + " links", { color: "var(--accent-lo)" })}</div>}>
          </window.PageHead>
          <EntityTable nodes={g.nodes} edges={g.edges}></EntityTable>
        </div>
      );
    }
    // not yet formed
    const arts = I.artifactsFor(proj).filter((a) => a.status === "parsed");
    return (
      <div>
        <window.PageHead eyebrow={tenant.name + " · " + proj.name} title="Entity Registry"
          sub="The live objects resolved from this project's parsed artifacts. The registry fills once the Knowledge Graph is built; the abstract type catalog is under Platform · Ontology Reference."
          right={<div style={{ textAlign: "right" }}>{erMono("no objects yet", { color: "var(--accent-lo)" })}</div>}>
        </window.PageHead>
        <div style={{ border: "1px dashed var(--rule-hard)", borderRadius: "var(--r-3)", background: "var(--bg)", padding: "34px 24px", textAlign: "center" }}>
          <div style={{ fontFamily: "var(--ui-display)", fontWeight: 600, fontSize: "var(--fs-md)", color: "var(--ink)", marginBottom: 6 }}>Registry not yet formed</div>
          <p style={{ margin: "0 auto 14px", maxWidth: 460, fontSize: "var(--fs-sm)", color: "var(--ink-3)", lineHeight: 1.6 }}>Objects resolve once the Knowledge Graph is built in Workflow · 01. This project is at the <b style={{ color: "var(--ink)" }}>{proj.stage}</b> stage{arts.length ? " — " + arts.length + " artifact" + (arts.length > 1 ? "s" : "") + " parsed" : ""}.</p>
          <button onClick={() => go("workflow")} style={{ border: "1px solid var(--accent-bd)", background: "var(--accent-bg)", color: "var(--accent-lo)", borderRadius: "var(--r-2)", padding: "8px 16px", cursor: "pointer", fontFamily: "var(--mono)", fontSize: "9.5px", fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase" }}>open workflow ↗</button>
        </div>
      </div>
    );
  }

  Object.assign(window, { EntityRegistry, RaisableEntityRegistry });
})();
