// ============ LEARNING COLLECTIONS TAB ============
// Learning Collection Plan, Issue 10: candidate queue (dismiss/ingest) +
// Collection management (list/edit/apply-to-agents). Mirrors approvals.jsx's
// component vocabulary (.ap-page, .ap-head, .ad-ls-row, .ad-ls-content) since
// this is the same target user — whoever manages agent learning quality via
// the approval queue — reviewing a different kind of item.
// See ~/.gstack/projects/DomaEGY03-Citrus/learning-collection-plan-20260712-113220.md

function LearningCollectionsTab({ agents = [], userRole, authUser }) {
  const canManage = ["Owner", "Admin", "MasterAdmin"].includes(userRole || authUser?.role);
  const byId = Object.fromEntries(agents.map((a) => [a.id, a]));

  const [candidates, setCandidates] = useState([]);
  const [collections, setCollections] = useState([]);
  const [loading, setLoading] = useState(true);

  const [ingestingId, setIngestingId] = useState(null); // candidate.id currently showing its ingest form
  const [ingestMode, setIngestMode]   = useState("new"); // "new" | "existing"
  const [ingestName, setIngestName]   = useState("");
  const [ingestDesc, setIngestDesc]   = useState("");
  const [ingestTargetCollectionId, setIngestTargetCollectionId] = useState("");
  const [ingestBusy, setIngestBusy]   = useState(false);

  const [editingId, setEditingId]     = useState(null); // collection.id currently being edited
  const [editName, setEditName]       = useState("");
  const [editDesc, setEditDesc]       = useState("");
  const [editBusy, setEditBusy]       = useState(false);

  const [applyingId, setApplyingId]   = useState(null); // collection.id showing its agent picker
  const [applySelected, setApplySelected] = useState(new Set());
  const [applyBusy, setApplyBusy]     = useState(false);
  const [applyResult, setApplyResult] = useState(null); // { collectionId, results: [...] }
  const [packLifecycleBusy, setPackLifecycleBusy] = useState(null);
  const [smartDraft, setSmartDraft] = useState({ open: false, name: "", membershipMode: "most_applied" });

  // ---- manual selection: hand-pick individual learnings -> Collection (CIT-512 Part B) ----
  const [manualOpen, setManualOpen]   = useState(false);
  const [manualSelected, setManualSelected] = useState(new Set()); // Set of "agentId::learningId"
  const [manualMode, setManualMode]   = useState("new"); // "new" | "existing"
  const [manualName, setManualName]   = useState("");
  const [manualDesc, setManualDesc]   = useState("");
  const [manualTargetCollectionId, setManualTargetCollectionId] = useState("");
  const [manualBusy, setManualBusy]   = useState(false);

  // ---- CSV upload: bulk-create brand-new learnings straight into a Collection ----
  const [csvOpen, setCsvOpen]         = useState(false);
  const [csvMode, setCsvMode]         = useState("new"); // "new" | "existing"
  const [csvName, setCsvName]         = useState("");
  const [csvDesc, setCsvDesc]         = useState("");
  const [csvTargetCollectionId, setCsvTargetCollectionId] = useState("");
  const [csvFileName, setCsvFileName] = useState("");
  const [csvContent, setCsvContent]   = useState("");
  const [csvBusy, setCsvBusy]         = useState(false);
  const csvFileInputRef = useRef(null);

  // ---- delete collection ----
  const [deletingId, setDeletingId]   = useState(null); // collection.id currently being deleted

  // Only status === "active", non-disabled learnings are selectable — matches
  // the trust bar the server route enforces (server/learning-collections.js's
  // resolveManualSelection), so nothing a user picks here can be rejected
  // silently for a reason they couldn't see in this list.
  const selectableLearnings = agents.flatMap((a) =>
    (a.learnings || [])
      .filter((l) => l.status === "active" && !l.disabled)
      .map((l) => ({ agentId: a.id, agentName: a.name, learningId: l.id, what: l.what })),
  );

  const reload = () => {
    setLoading(true);
    Promise.all([
      fetch("/collection-candidates").then((r) => (r.ok ? r.json() : [])).catch(() => []),
      fetch("/learning-collections").then((r) => (r.ok ? r.json() : [])).catch(() => []),
    ])
      .then(([cands, cols]) => { setCandidates(cands); setCollections(cols); })
      .finally(() => setLoading(false));
  };

  useEffect(() => { reload(); }, []);

  const fmtTime = (iso) =>
    iso ? new Date(iso).toLocaleString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" }) : "";

  // ---- candidate actions ----
  const dismissCandidate = (id) => {
    fetch(`/collection-candidates/${id}/dismiss`, { method: "POST" })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })).catch(() => ({ ok: r.ok, j: {} })))
      .then(({ ok, j }) => {
        if (!ok) { window.toast && window.toast(j?.error || "Couldn't dismiss this candidate", "warn"); return; }
        setCandidates((prev) => prev.filter((c) => c.id !== id));
        window.toast && window.toast("Candidate dismissed", "good");
      })
      .catch(() => window.toast && window.toast("Couldn't dismiss this candidate", "warn"));
  };

  const openIngest = (id) => {
    setIngestingId(id);
    setIngestMode("new");
    setIngestName("");
    setIngestDesc("");
    setIngestTargetCollectionId(collections[0]?.id || "");
  };
  const closeIngest = () => setIngestingId(null);

  const confirmIngest = (candidate) => {
    if (ingestMode === "new" && !ingestName.trim()) {
      window.toast && window.toast("Name the Collection first", "warn");
      return;
    }
    if (ingestMode === "existing" && !ingestTargetCollectionId) {
      window.toast && window.toast("Pick a Collection first", "warn");
      return;
    }
    setIngestBusy(true);
    const body = ingestMode === "existing"
      ? { mode: "existing", collectionId: ingestTargetCollectionId }
      : { mode: "new", name: ingestName.trim(), description: ingestDesc.trim() };
    fetch(`/collection-candidates/${candidate.id}/ingest`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(body),
    })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })).catch(() => ({ ok: r.ok, j: {} })))
      .then(({ ok, j }) => {
        if (!ok) { window.toast && window.toast(j?.error || "Couldn't ingest this candidate", "warn"); return; }
        setCandidates((prev) => prev.filter((c) => c.id !== candidate.id));
        closeIngest();
        window.toast && window.toast(
          ingestMode === "existing" ? "Added to Collection" : "New Collection created",
          "good",
        );
        reload();
      })
      .catch(() => window.toast && window.toast("Couldn't ingest this candidate", "warn"))
      .finally(() => setIngestBusy(false));
  };

  // ---- collection actions ----
  const openEdit = (collection) => {
    setEditingId(collection.id);
    setEditName(collection.name);
    setEditDesc(collection.description || "");
  };
  const closeEdit = () => setEditingId(null);

  const confirmEdit = (collection) => {
    if (!editName.trim()) { window.toast && window.toast("Name cannot be blank", "warn"); return; }
    setEditBusy(true);
    fetch(`/learning-collections/${collection.id}`, {
      method: "PATCH",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ name: editName.trim(), description: editDesc.trim() }),
    })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })).catch(() => ({ ok: r.ok, j: {} })))
      .then(({ ok, j }) => {
        if (!ok) { window.toast && window.toast(j?.error || "Couldn't save changes", "warn"); return; }
        setCollections((prev) => prev.map((c) => (c.id === collection.id ? j.collection : c)));
        closeEdit();
        window.toast && window.toast("Collection updated", "good");
      })
      .catch(() => window.toast && window.toast("Couldn't save changes", "warn"))
      .finally(() => setEditBusy(false));
  };

  const openApply = (collection) => {
    setApplyingId(collection.id);
    setApplySelected(new Set());
    setApplyResult(null);
  };
  const closeApply = () => setApplyingId(null);

  const toggleApplyAgent = (agentId) => {
    setApplySelected((prev) => {
      const next = new Set(prev);
      if (next.has(agentId)) next.delete(agentId); else next.add(agentId);
      return next;
    });
  };

  const confirmApply = (collection) => {
    const targetAgentIds = [...applySelected];
    if (targetAgentIds.length === 0) {
      window.toast && window.toast("Pick at least one agent", "warn");
      return;
    }
    setApplyBusy(true);
    fetch(`/learning-collections/${collection.id}/apply`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ targetAgentIds }),
    })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })).catch(() => ({ ok: r.ok, j: {} })))
      .then(({ ok, j }) => {
        if (!ok) { window.toast && window.toast(j?.error || "Couldn't apply this Collection", "warn"); return; }
        if (j.queued) {
          window.toast && window.toast(`Queued — applying to ${j.targetCount} agents in the background`, "good");
          closeApply();
          return;
        }
        setApplyResult({ collectionId: collection.id, results: j.results || [] });
        const succeeded = (j.results || []).filter((r) => r.ok).length;
        const failed = (j.results || []).length - succeeded;
        window.toast && window.toast(
          failed > 0 ? `Applied to ${succeeded} agent(s), ${failed} failed` : `Applied to ${succeeded} agent(s)`,
          failed > 0 ? "warn" : "good",
        );
        reload();
      })
      .catch(() => window.toast && window.toast("Couldn't apply this Collection", "warn"))
      .finally(() => setApplyBusy(false));
  };

  const refreshSmartPack = (collection) => {
    setPackLifecycleBusy(collection.id);
    fetch(`/learning-collections/${collection.id}/refresh-smart`, { method: "POST" })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })))
      .then(({ ok, j }) => {
        if (!ok) throw new Error(j?.error || "Couldn't refresh Smart Pack");
        window.toast && window.toast(`Smart Pack refreshed with ${j.ranked} learning(s)`, "good");
        reload();
      })
      .catch((error) => window.toast && window.toast(error.message, "warn"))
      .finally(() => setPackLifecycleBusy(null));
  };

  const createSmartPack = () => {
    if (!smartDraft.name.trim()) return;
    setPackLifecycleBusy("create-smart");
    fetch("/learning-collections", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ name: smartDraft.name.trim(), membershipMode: smartDraft.membershipMode }),
    })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })))
      .then(async ({ ok, j }) => {
        if (!ok) throw new Error(j?.error || "Couldn't create Smart Pack");
        const response = await fetch(`/learning-collections/${j.collection.id}/refresh-smart`, { method: "POST" });
        const refreshed = await response.json();
        if (!response.ok) throw new Error(refreshed?.error || "Smart Pack created but couldn't be refreshed");
        window.toast && window.toast(`Created Smart Pack with ${refreshed.ranked} learning(s)`, "good");
        setSmartDraft({ open: false, name: "", membershipMode: "most_applied" });
        reload();
      })
      .catch((error) => window.toast && window.toast(error.message, "warn"))
      .finally(() => setPackLifecycleBusy(null));
  };

  const upgradeAssignedAgents = (collection) => {
    setPackLifecycleBusy(collection.id);
    fetch(`/learning-collections/${collection.id}/upgrades`)
      .then((r) => r.json())
      .then(async (data) => {
        const targetAgentIds = (data.upgrades || []).filter((item) => item.preview?.ok).map((item) => item.agentId);
        if (targetAgentIds.length === 0) {
          window.toast && window.toast((data.upgrades || []).length ? "All available upgrades are blocked by conflicts" : "All agents already use this version", "warn");
          return;
        }
        const response = await fetch(`/learning-collections/${collection.id}/upgrade`, {
          method: "POST",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ targetAgentIds }),
        });
        const result = await response.json();
        if (!response.ok) throw new Error(result?.error || "Couldn't upgrade Pack assignments");
        window.toast && window.toast(`Upgraded ${targetAgentIds.length} agent(s) to Pack v${collection.version || 1}`, "good");
        reload();
      })
      .catch((error) => window.toast && window.toast(error.message, "warn"))
      .finally(() => setPackLifecycleBusy(null));
  };

  const rollbackPack = (collection, version) => {
    setPackLifecycleBusy(collection.id);
    fetch(`/learning-collections/${collection.id}/rollback/${version}`, { method: "POST" })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })))
      .then(({ ok, j }) => {
        if (!ok) throw new Error(j?.error || "Couldn't restore Pack version");
        window.toast && window.toast(`Restored Pack v${version} as a new version`, "good");
        reload();
      })
      .catch((error) => window.toast && window.toast(error.message, "warn"))
      .finally(() => setPackLifecycleBusy(null));
  };

  // ---- manual selection actions ----
  const openManual = () => {
    setManualOpen(true);
    setManualSelected(new Set());
    setManualMode("new");
    setManualName("");
    setManualDesc("");
    setManualTargetCollectionId(collections[0]?.id || "");
  };
  const closeManual = () => setManualOpen(false);

  const toggleManualSelect = (agentId, learningId) => {
    const key = `${agentId}::${learningId}`;
    setManualSelected((prev) => {
      const next = new Set(prev);
      if (next.has(key)) next.delete(key); else next.add(key);
      return next;
    });
  };

  const confirmManual = () => {
    const selections = [...manualSelected].map((key) => {
      const [agentId, learningId] = key.split("::");
      return { agentId, learningId };
    });
    if (selections.length === 0) {
      window.toast && window.toast("Select at least one learning", "warn");
      return;
    }
    if (manualMode === "new" && !manualName.trim()) {
      window.toast && window.toast("Name the Collection first", "warn");
      return;
    }
    if (manualMode === "existing" && !manualTargetCollectionId) {
      window.toast && window.toast("Pick a Collection first", "warn");
      return;
    }
    setManualBusy(true);
    const body = manualMode === "existing"
      ? { mode: "existing", collectionId: manualTargetCollectionId, selections }
      : { mode: "new", name: manualName.trim(), description: manualDesc.trim(), selections };
    fetch("/learning-collections/manual", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(body),
    })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })).catch(() => ({ ok: r.ok, j: {} })))
      .then(({ ok, j }) => {
        if (!ok) { window.toast && window.toast(j?.error || "Couldn't create the Collection", "warn"); return; }
        closeManual();
        const skippedCount = (j.skipped || []).length;
        window.toast && window.toast(
          skippedCount > 0 ? `Saved — ${skippedCount} selection(s) skipped (no longer active)` : "Collection saved",
          skippedCount > 0 ? "warn" : "good",
        );
        reload();
      })
      .catch(() => window.toast && window.toast("Couldn't create the Collection", "warn"))
      .finally(() => setManualBusy(false));
  };

  // ---- CSV upload actions ----
  const openCsv = () => {
    setCsvOpen(true);
    setCsvMode("new");
    setCsvName("");
    setCsvDesc("");
    setCsvTargetCollectionId(collections[0]?.id || "");
    setCsvFileName("");
    setCsvContent("");
  };
  const closeCsv = () => setCsvOpen(false);

  const pickCsvFile = () => csvFileInputRef.current && csvFileInputRef.current.click();

  const onCsvFileChosen = (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    setCsvFileName(file.name);
    const reader = new FileReader();
    reader.onload = () => setCsvContent(String(reader.result || ""));
    reader.onerror = () => window.toast && window.toast("Couldn't read that file", "warn");
    reader.readAsText(file);
    e.target.value = ""; // allow re-choosing the exact same file later
  };

  const confirmCsv = () => {
    if (!csvContent.trim()) { window.toast && window.toast("Choose a CSV file first", "warn"); return; }
    if (csvMode === "new" && !csvName.trim()) { window.toast && window.toast("Name the Collection first", "warn"); return; }
    if (csvMode === "existing" && !csvTargetCollectionId) { window.toast && window.toast("Pick a Collection first", "warn"); return; }
    setCsvBusy(true);
    const body = csvMode === "existing"
      ? { mode: "existing", collectionId: csvTargetCollectionId, content: csvContent }
      : { mode: "new", name: csvName.trim(), description: csvDesc.trim(), content: csvContent };
    fetch("/learning-collections/csv-upload", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(body),
    })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })).catch(() => ({ ok: r.ok, j: {} })))
      .then(({ ok, j }) => {
        if (!ok) { window.toast && window.toast(j?.error || "Couldn't upload that CSV", "warn"); return; }
        closeCsv();
        const skippedCount = (j.skipped || []).length;
        window.toast && window.toast(
          skippedCount > 0 ? `Imported ${j.imported} row(s), skipped ${skippedCount}` : `Imported ${j.imported} row(s)`,
          skippedCount > 0 ? "warn" : "good",
        );
        reload();
      })
      .catch(() => window.toast && window.toast("Couldn't upload that CSV", "warn"))
      .finally(() => setCsvBusy(false));
  };

  // ---- delete collection ----
  const deleteCollection = (collection) => {
    if (!window.confirm(`Delete "${collection.name}"? This can't be undone. Agents it's already been applied to keep what they've learned.`)) return;
    setDeletingId(collection.id);
    fetch(`/learning-collections/${collection.id}`, { method: "DELETE" })
      .then((r) => r.json().then((j) => ({ ok: r.ok, j })).catch(() => ({ ok: r.ok, j: {} })))
      .then(({ ok, j }) => {
        if (!ok) { window.toast && window.toast(j?.error || "Couldn't delete this Collection", "warn"); return; }
        setCollections((prev) => prev.filter((c) => c.id !== collection.id));
        window.toast && window.toast("Collection deleted", "good");
      })
      .catch(() => window.toast && window.toast("Couldn't delete this Collection", "warn"))
      .finally(() => setDeletingId(null));
  };

  // ---- render ----
  if (loading) {
    return <div className="ap-empty"><div className="ap-empty-s">Loading…</div></div>;
  }

  const renderCandidateRow = (candidate) => {
    const whatSummary = candidate.learnings?.[0]?.what || "Reappearing learning";
    const sourceNames = (candidate.sourceAgents || []).map((a) => byId[a.id]?.name || a.name).join(", ");
    const isIngesting = ingestingId === candidate.id;

    return (
      <div key={candidate.id} className="ad-ls-row" style={{ flexWrap: "wrap", alignItems: "flex-start", paddingTop: 14, paddingBottom: 14 }}>
        <div className="ad-ls-content">
          <div className="ad-ls-what">{whatSummary}</div>
          <div className="ad-ls-meta">
            <span>{candidate.learnings?.length || 0} matching learning(s) across {candidate.agentIds?.length || 0} agents</span>
            <span>· seen on: {sourceNames}</span>
            <span>· {fmtTime(candidate.createdAt)}</span>
          </div>
        </div>
        {canManage && (
          <div style={{ display: "flex", gap: 6, alignItems: "center", flexShrink: 0 }}>
            <button className="btn btn-sm btn-primary" onClick={() => openIngest(candidate.id)}>Promote</button>
            <button className="btn btn-sm btn-ghost" onClick={() => dismissCandidate(candidate.id)}>Dismiss</button>
          </div>
        )}
        {isIngesting && (
          <div style={{ flexBasis: "100%", marginTop: 10, padding: 12, background: "var(--paper-2)", borderRadius: 12 }}>
            <div style={{ display: "flex", gap: 12, marginBottom: 8 }}>
              <label style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 13 }}>
                <input type="radio" checked={ingestMode === "new"} onChange={() => setIngestMode("new")} /> New Collection
              </label>
              <label style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 13 }}>
                <input type="radio" checked={ingestMode === "existing"} onChange={() => setIngestMode("existing")} disabled={collections.length === 0} />
                Add to existing {collections.length === 0 ? "(none yet)" : ""}
              </label>
            </div>
            {ingestMode === "new" ? (
              <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                <input className="apr-input" style={{ width: "100%", maxWidth: 320 }} placeholder="Collection name" value={ingestName} onChange={(e) => setIngestName(e.target.value)} />
                <input className="apr-input" style={{ width: "100%", maxWidth: 320 }} placeholder="Description (optional)" value={ingestDesc} onChange={(e) => setIngestDesc(e.target.value)} />
              </div>
            ) : (
              <select className="apr-input" style={{ width: "100%", maxWidth: 320 }} value={ingestTargetCollectionId} onChange={(e) => setIngestTargetCollectionId(e.target.value)}>
                {collections.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
              </select>
            )}
            <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
              <button className="btn btn-sm btn-primary" disabled={ingestBusy} onClick={() => confirmIngest(candidate)}>
                {ingestBusy ? "Working…" : "Confirm"}
              </button>
              <button className="btn btn-sm btn-ghost" onClick={closeIngest}>Cancel</button>
            </div>
          </div>
        )}
      </div>
    );
  };

  const renderCollectionCard = (collection) => {
    const isEditing = editingId === collection.id;
    const isApplying = applyingId === collection.id;
    const result = applyResult && applyResult.collectionId === collection.id ? applyResult.results : null;

    return (
      <div key={collection.id} className="ad-ls-row" style={{ flexWrap: "wrap", alignItems: "flex-start", paddingTop: 14, paddingBottom: 14 }}>
        <div className="ad-ls-content">
          {isEditing ? (
            <div style={{ display: "flex", flexDirection: "column", gap: 6, maxWidth: 360 }}>
              <input className="apr-input" style={{ width: "100%", maxWidth: 320 }} value={editName} onChange={(e) => setEditName(e.target.value)} />
              <input className="apr-input" style={{ width: "100%", maxWidth: 320 }} value={editDesc} onChange={(e) => setEditDesc(e.target.value)} placeholder="Description" />
              <div style={{ display: "flex", gap: 6 }}>
                <button className="btn btn-sm btn-primary" disabled={editBusy} onClick={() => confirmEdit(collection)}>{editBusy ? "Saving…" : "Save"}</button>
                <button className="btn btn-sm btn-ghost" onClick={closeEdit}>Cancel</button>
              </div>
            </div>
          ) : (
            <>
              <div className="ad-ls-what">{collection.name}</div>
              <div className="ad-ls-meta">
                <span>{collection.learnings?.length || 0} learning(s)</span>
                <span>· applied to {collection.appliedTo?.length || 0} agent(s)</span>
                <span>· v{collection.version || 1}</span>
                <span>· {(collection.membershipMode || "manual").replaceAll("_", " ")}</span>
                {collection.description ? <span>· {collection.description}</span> : null}
              </div>
            </>
          )}
        </div>
        {canManage && !isEditing && (
          <div style={{ display: "flex", gap: 6, alignItems: "center", flexShrink: 0 }}>
            <button className="btn btn-sm btn-ghost" onClick={() => openEdit(collection)}>Edit</button>
            <button className="btn btn-sm btn-primary" onClick={() => openApply(collection)}>Apply to agents</button>
            {collection.membershipMode && collection.membershipMode !== "manual" ? (
              <button className="btn btn-sm btn-ghost" disabled={packLifecycleBusy === collection.id} onClick={() => refreshSmartPack(collection)}>Refresh smart membership</button>
            ) : null}
            <button className="btn btn-sm btn-ghost" disabled={packLifecycleBusy === collection.id} onClick={() => upgradeAssignedAgents(collection)}>Review upgrades</button>
            {collection.versionHistory?.find((version) => Array.isArray(version.learnings)) ? (
              <button className="btn btn-sm btn-ghost" disabled={packLifecycleBusy === collection.id} onClick={() => rollbackPack(collection, collection.versionHistory.find((version) => Array.isArray(version.learnings)).version)}>Restore previous</button>
            ) : null}
            <button
              className="btn btn-sm btn-ghost"
              style={{ color: "#C04545" }}
              disabled={deletingId === collection.id}
              onClick={() => deleteCollection(collection)}
            >
              {deletingId === collection.id ? "Deleting…" : "Delete"}
            </button>
          </div>
        )}
        {isApplying && (
          <div style={{ flexBasis: "100%", marginTop: 10, padding: 12, background: "var(--paper-2)", borderRadius: 12 }}>
            <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6, color: "var(--ink-2)" }}>Pick target agents</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 4, maxHeight: 180, overflowY: "auto", marginBottom: 8 }}>
              {agents.map((a) => (
                <label key={a.id} style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 13 }}>
                  <input type="checkbox" checked={applySelected.has(a.id)} onChange={() => toggleApplyAgent(a.id)} />
                  {a.name}
                </label>
              ))}
            </div>
            <div style={{ display: "flex", gap: 6 }}>
              <button className="btn btn-sm btn-primary" disabled={applyBusy} onClick={() => confirmApply(collection)}>
                {applyBusy ? "Applying…" : `Apply to ${applySelected.size || 0} agent(s)`}
              </button>
              <button className="btn btn-sm btn-ghost" onClick={closeApply}>Cancel</button>
            </div>
            {result && (
              <div style={{ marginTop: 8, fontSize: 12 }}>
                {result.map((r) => (
                  <div key={r.agentId} style={{ color: r.ok ? "#2DAF6B" : "#C04545" }}>
                    {byId[r.agentId]?.name || r.agentId}: {r.ok ? `+${r.added} learning(s)` : r.error}
                  </div>
                ))}
              </div>
            )}
          </div>
        )}
      </div>
    );
  };

  return (
    <div className="ap-page">
      <div className="ap-head">
        <div>
          <div className="ap-head-c">Learning Packs</div>
          <div className="ap-head-l">
            {candidates.length} candidate{candidates.length === 1 ? "" : "s"} awaiting review · {collections.length} Pack{collections.length === 1 ? "" : "s"}
          </div>
        </div>
      </div>

      {canManage && (
        <div className="ad-section-block" style={{ marginBottom: 16 }}>
          <div className="ad-section-hdr" style={{ marginBottom: 8 }}>
            <span style={{ fontWeight: 700, fontSize: 13, color: "var(--ink)" }}>Smart Learning Pack</span>
            {!smartDraft.open ? <button className="btn btn-sm btn-primary" onClick={() => setSmartDraft({ ...smartDraft, open: true })}>Create Smart Pack</button> : null}
          </div>
          {smartDraft.open ? <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            <input className="apr-input" placeholder="Pack name" value={smartDraft.name} onChange={(e) => setSmartDraft({ ...smartDraft, name: e.target.value })} />
            <select className="apr-input" value={smartDraft.membershipMode} onChange={(e) => setSmartDraft({ ...smartDraft, membershipMode: e.target.value })}>
              <option value="most_applied">Most applied</option>
              <option value="most_used">Most used</option>
            </select>
            <button className="btn btn-sm btn-primary" disabled={packLifecycleBusy === "create-smart"} onClick={createSmartPack}>Create and analyze</button>
            <button className="btn btn-sm btn-ghost" onClick={() => setSmartDraft({ open: false, name: "", membershipMode: "most_applied" })}>Cancel</button>
          </div> : null}
        </div>
      )}

      {canManage && (
        <div className="ad-section-block" style={{ marginBottom: 16 }}>
          <div className="ad-section-hdr" style={{ marginBottom: 8 }}>
            <span style={{ fontWeight: 700, fontSize: 13, color: "var(--ink)" }}>Manual selection</span>
            {!manualOpen && (
              <button className="btn btn-sm btn-primary" onClick={openManual}>Create pack from selection</button>
            )}
          </div>
          {manualOpen && (
            <div style={{ padding: 12, background: "var(--paper-2)", borderRadius: 12 }}>
              <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6, color: "var(--ink-2)" }}>
                Pick individual learnings from any agent(s)
              </div>
              <div style={{ display: "flex", flexDirection: "column", gap: 4, maxHeight: 220, overflowY: "auto", marginBottom: 10 }}>
                {selectableLearnings.length === 0 ? (
                  <div style={{ fontSize: 12, color: "var(--ink-soft)" }}>No active learnings available to select.</div>
                ) : selectableLearnings.map((row) => {
                  const key = `${row.agentId}::${row.learningId}`;
                  return (
                    <label key={key} style={{ display: "flex", alignItems: "flex-start", gap: 6, fontSize: 13 }}>
                      <input type="checkbox" checked={manualSelected.has(key)} onChange={() => toggleManualSelect(row.agentId, row.learningId)} style={{ marginTop: 3 }} />
                      <span><strong>{row.agentName}</strong> — {row.what}</span>
                    </label>
                  );
                })}
              </div>
              <div style={{ display: "flex", gap: 12, marginBottom: 8 }}>
                <label style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 13 }}>
                  <input type="radio" checked={manualMode === "new"} onChange={() => setManualMode("new")} /> New Pack
                </label>
                <label style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 13 }}>
                  <input type="radio" checked={manualMode === "existing"} onChange={() => setManualMode("existing")} disabled={collections.length === 0} />
                  Add to existing {collections.length === 0 ? "(none yet)" : ""}
                </label>
              </div>
              {manualMode === "new" ? (
                <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                  <input className="apr-input" style={{ width: "100%", maxWidth: 320 }} placeholder="Pack name" value={manualName} onChange={(e) => setManualName(e.target.value)} />
                  <input className="apr-input" style={{ width: "100%", maxWidth: 320 }} placeholder="Description (optional)" value={manualDesc} onChange={(e) => setManualDesc(e.target.value)} />
                </div>
              ) : (
                <select className="apr-input" style={{ width: "100%", maxWidth: 320 }} value={manualTargetCollectionId} onChange={(e) => setManualTargetCollectionId(e.target.value)}>
                  {collections.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
                </select>
              )}
              <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
                <button className="btn btn-sm btn-primary" disabled={manualBusy} onClick={confirmManual}>
                  {manualBusy ? "Working…" : `Create from ${manualSelected.size} selected`}
                </button>
                <button className="btn btn-sm btn-ghost" onClick={closeManual}>Cancel</button>
              </div>
            </div>
          )}
        </div>
      )}

      {canManage && (
        <div className="ad-section-block" style={{ marginBottom: 16 }}>
          <div className="ad-section-hdr" style={{ marginBottom: 8 }}>
            <span style={{ fontWeight: 700, fontSize: 13, color: "var(--ink)" }}>CSV upload</span>
            {!csvOpen && (
              <button className="btn btn-sm btn-primary" onClick={openCsv}>Upload a batch of learnings</button>
            )}
          </div>
          {csvOpen && (
            <div style={{ padding: 12, background: "var(--paper-2)", borderRadius: 12 }}>
              <div style={{ fontSize: 12, color: "var(--ink-2)", marginBottom: 10 }}>
                Upload a CSV with a <code>what,why</code> header row (why may be blank) — each row becomes a new learning in the Pack. Same format as Company Rules' bulk import, so an export from there can be reused here too.
              </div>
              <input ref={csvFileInputRef} type="file" accept=".csv,text/csv" style={{ display: "none" }} onChange={onCsvFileChosen} />
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
                <button className="btn btn-sm btn-ghost" onClick={pickCsvFile}>Choose file</button>
                <span style={{ fontSize: 12, color: "var(--ink-soft)" }}>{csvFileName || "No file chosen"}</span>
              </div>
              <div style={{ display: "flex", gap: 12, marginBottom: 8 }}>
                <label style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 13 }}>
                  <input type="radio" checked={csvMode === "new"} onChange={() => setCsvMode("new")} /> New Pack
                </label>
                <label style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 13 }}>
                  <input type="radio" checked={csvMode === "existing"} onChange={() => setCsvMode("existing")} disabled={collections.length === 0} />
                  Add to existing {collections.length === 0 ? "(none yet)" : ""}
                </label>
              </div>
              {csvMode === "new" ? (
                <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                  <input className="apr-input" style={{ width: "100%", maxWidth: 320 }} placeholder="Collection name" value={csvName} onChange={(e) => setCsvName(e.target.value)} />
                  <input className="apr-input" style={{ width: "100%", maxWidth: 320 }} placeholder="Description (optional)" value={csvDesc} onChange={(e) => setCsvDesc(e.target.value)} />
                </div>
              ) : (
                <select className="apr-input" style={{ width: "100%", maxWidth: 320 }} value={csvTargetCollectionId} onChange={(e) => setCsvTargetCollectionId(e.target.value)}>
                  {collections.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
                </select>
              )}
              <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
                <button className="btn btn-sm btn-primary" disabled={csvBusy} onClick={confirmCsv}>
                  {csvBusy ? "Uploading…" : "Upload"}
                </button>
                <button className="btn btn-sm btn-ghost" onClick={closeCsv}>Cancel</button>
              </div>
            </div>
          )}
        </div>
      )}

      <div className="ad-section-block" style={{ marginBottom: 16 }}>
        <div className="ad-section-hdr" style={{ marginBottom: 8 }}>
          <span style={{ fontWeight: 700, fontSize: 13, color: "var(--ink)" }}>Candidates</span>
          <span style={{ fontSize: 11, color: "var(--ink-soft)" }}>{candidates.length}</span>
        </div>
        {candidates.length === 0 ? (
          <div className="ap-empty">
            <div className="ap-empty-t">No candidates yet</div>
            <div className="ap-empty-s">A learning reappearing across trusted agents will show up here for review.</div>
          </div>
        ) : (
          <div className="ad-learnings-list">{candidates.map(renderCandidateRow)}</div>
        )}
      </div>

      <div className="ad-section-block">
        <div className="ad-section-hdr" style={{ marginBottom: 8 }}>
          <span style={{ fontWeight: 700, fontSize: 13, color: "var(--ink)" }}>Collections</span>
          <span style={{ fontSize: 11, color: "var(--ink-soft)" }}>{collections.length}</span>
        </div>
        {collections.length === 0 ? (
          <div className="ap-empty">
            <div className="ap-empty-t">No Collections yet</div>
            <div className="ap-empty-s">Promote a candidate above to create your first Collection.</div>
          </div>
        ) : (
          <div className="ad-learnings-list">{collections.map(renderCollectionCard)}</div>
        )}
      </div>
    </div>
  );
}

window.LearningCollectionsTab = LearningCollectionsTab;
