// Atlas · Enterprise Internal Repository
const { useState: useStateRepo, useEffect: useEffectRepo, useRef: useRefRepo } = React;

function renderSummaryMarkdown(text) {
  if (!text) return null;
  const mono = { fontFamily: "var(--mono)", fontSize: 12 };
  const prose = { fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" };
  const elements = [];
  const lines = text.split("\n");
  let i = 0;

  const formatInline = (str) => {
    const parts = [];
    const re = /(\*\*(.+?)\*\*|`(.+?)`|\*(.+?)\*)/g;
    let last = 0, m;
    while ((m = re.exec(str)) !== null) {
      if (m.index > last) parts.push(str.slice(last, m.index));
      if (m[2] !== undefined) parts.push(React.createElement("strong", { key: m.index }, m[2]));
      else if (m[3] !== undefined) parts.push(React.createElement("code", { key: m.index, style: { ...mono, background: "var(--bg-2)", padding: "1px 4px", borderRadius: 3, fontSize: 11 } }, m[3]));
      else if (m[4] !== undefined) parts.push(React.createElement("em", { key: m.index }, m[4]));
      last = m.index + m[0].length;
    }
    if (last < str.length) parts.push(str.slice(last));
    return parts.length === 1 && typeof parts[0] === "string" ? parts[0] : parts;
  };

  while (i < lines.length) {
    const line = lines[i];
    if (!line.trim()) { i++; continue; }

    if (line.startsWith("## ")) {
      elements.push(React.createElement("div", { key: i, style: { ...prose, fontWeight: 700, fontSize: 14, color: "var(--ink)", marginTop: 4, marginBottom: 10, borderBottom: "1px solid var(--line-2)", paddingBottom: 6 } }, formatInline(line.slice(3))));
    } else if (line.startsWith("### ")) {
      elements.push(React.createElement("div", { key: i, style: { ...prose, fontWeight: 600, fontSize: 13, color: "var(--ink)", marginTop: 16, marginBottom: 6 } }, formatInline(line.slice(4))));
    } else if (line.startsWith("- ")) {
      elements.push(React.createElement("div", { key: i, style: { ...prose, fontSize: 13, display: "flex", gap: 6, marginBottom: 5, lineHeight: 1.6 } },
        React.createElement("span", { style: { color: "var(--ink-3)", flexShrink: 0 } }, "–"),
        React.createElement("span", { style: { color: "var(--ink-2)" } }, formatInline(line.slice(2)))
      ));
    } else {
      elements.push(React.createElement("div", { key: i, style: { ...prose, fontSize: 13, color: "var(--ink-2)", marginBottom: 3, lineHeight: 1.6 } }, formatInline(line)));
    }
    i++;
  }
  return elements;
}

function formatActivityDate(ts) {
  if (!ts) return "";
  const d = new Date(ts);
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
}

function Repository({ user, isRepoAdmin, isOrgAdmin }) {
  const [tab, setTab] = useStateRepo("documents");
  const [documents, setDocuments] = useStateRepo([]);
  const [summary, setSummary] = useStateRepo("");
  const [activityLog, setActivityLog] = useStateRepo([]);
  const [teamData, setTeamData] = useStateRepo(null);
  const [loading, setLoading] = useStateRepo(false);
  const [error, setError] = useStateRepo(null);
  const [summaryOpen, setSummaryOpen] = useStateRepo(false);
  const [howToOpen, setHowToOpen] = useStateRepo(false);
  const [activityOpen, setActivityOpen] = useStateRepo(false);
  const [searchQ, setSearchQ] = useStateRepo("");
  const [searchResults, setSearchResults] = useStateRepo([]);
  const [searchLoading, setSearchLoading] = useStateRepo(false);
  const [searchError, setSearchError] = useStateRepo(null);
  const [tagOverviewOpen, setTagOverviewOpen] = useStateRepo(false);
  const [guidelinesOpen, setGuidelinesOpen] = useStateRepo(false);
  const [uploadDragging, setUploadDragging] = useStateRepo(false);
  const [uploading, setUploading] = useStateRepo(false);
  const [uploadError, setUploadError] = useStateRepo(null);
  const [editingTitle, setEditingTitle] = useStateRepo({});
  const [editingTag, setEditingTag] = useStateRepo({});
  const [tagFilter, setTagFilter] = useStateRepo([]);
  const [summaryUpdating, setSummaryUpdating] = useStateRepo(false);
  const [standardTags, setStandardTags] = useStateRepo([]);
  const [tagGuideOpen, setTagGuideOpen] = useStateRepo(false);
  const [approveRoles, setApproveRoles] = useStateRepo({});
  const [inviteEmail, setInviteEmail] = useStateRepo("");
  const [inviteRole, setInviteRole] = useStateRepo("member");
  const [inviteStatus, setInviteStatus] = useStateRepo(null);
  const [inviteError, setInviteError] = useStateRepo(null);
  const fileInputRef = useRefRepo(null);
  const searchTimer = useRefRepo(null);

  useEffectRepo(() => { loadData(); }, []);

  const loadData = async () => {
    setLoading(true);
    setError(null);
    try {
      const [repoData, team] = await Promise.all([
        apiGet("/api/repository"),
        apiGet("/api/team").catch(() => null),
      ]);
      setDocuments(repoData.documents || []);
      setSummary(repoData.summary || "");
      setSummaryUpdating(repoData.summary_updating || false);
      setActivityLog(repoData.activity_log || []);
      setStandardTags(repoData.standard_tags || []);
      setTeamData(team);
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  };

  useEffectRepo(() => {
    if (!summaryUpdating) return;
    const id = setInterval(async () => {
      try {
        const data = await apiGet("/api/repository");
        setSummary(data.summary || "");
        setDocuments(data.documents || []);
        setActivityLog(data.activity_log || []);
        if (!data.summary_updating) {
          setSummaryUpdating(false);
          clearInterval(id);
        }
      } catch {}
    }, 4000);
    return () => clearInterval(id);
  }, [summaryUpdating]);

  useEffectRepo(() => {
    if (!searchQ.trim()) { setSearchResults([]); return; }
    clearTimeout(searchTimer.current);
    searchTimer.current = setTimeout(async () => {
      setSearchLoading(true);
      setSearchError(null);
      try {
        const data = await apiGet(`/api/repository/search?q=${encodeURIComponent(searchQ.trim())}`);
        setSearchResults(data.results || []);
      } catch (e) {
        setSearchError("Search unavailable — " + e.message);
        setSearchResults([]);
      } finally {
        setSearchLoading(false);
      }
    }, 400);
    return () => clearTimeout(searchTimer.current);
  }, [searchQ]);

  const handleUploadFile = async (file) => {
    setUploadError(null);
    setUploading(true);
    try {
      const fd = new FormData();
      fd.append("file", file);
      const res = await fetch("/api/repository/upload", { method: "POST", body: fd });
      if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        throw new Error(err.detail || "Upload failed");
      }
      const doc = await res.json();
      setDocuments(prev => [...prev, doc]);
      setSummaryUpdating(true);
    } catch (e) {
      setUploadError(e.message);
    } finally {
      setUploading(false);
    }
  };

  const handleDrop = (e) => {
    e.preventDefault();
    setUploadDragging(false);
    const file = e.dataTransfer.files[0];
    if (file) handleUploadFile(file);
  };

  const handleDelete = async (doc_id) => {
    if (!confirm("Delete this document? This cannot be undone.")) return;
    try {
      const res = await fetch(`/api/repository/${doc_id}`, { method: "DELETE" });
      if (!res.ok) throw new Error("Delete failed");
      setDocuments(prev => prev.filter(d => d.doc_id !== doc_id));
      setSummaryUpdating(true);
      // Refresh activity log after delete
      const data = await apiGet("/api/repository").catch(() => null);
      if (data) setActivityLog(data.activity_log || []);
    } catch (e) {
      alert(e.message);
    }
  };

  const handleTitleSave = async (doc_id, title) => {
    try {
      const res = await fetch(`/api/repository/${doc_id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ title }),
      });
      if (res.ok) {
        const updated = await res.json();
        setDocuments(prev => prev.map(d => d.doc_id === doc_id ? updated : d));
      }
    } catch {}
    setEditingTitle(prev => { const n = { ...prev }; delete n[doc_id]; return n; });
  };

  const handleTagsSave = async (doc_id, tags) => {
    try {
      const res = await fetch(`/api/repository/${doc_id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ tags }),
      });
      if (res.ok) {
        const updated = await res.json();
        setDocuments(prev => prev.map(d => d.doc_id === doc_id ? updated : d));
      }
    } catch {}
  };

  const handleAddTag = async (doc_id, newTag, currentTags) => {
    const trimmed = newTag.trim().toLowerCase();
    setEditingTag(prev => { const n = { ...prev }; delete n[doc_id]; return n; });
    if (!trimmed || currentTags.includes(trimmed)) return;
    await handleTagsSave(doc_id, [...currentTags, trimmed]);
  };

  const handleRemoveTag = async (doc_id, tag, currentTags) => {
    await handleTagsSave(doc_id, currentTags.filter(t => t !== tag));
  };

  const handleSuggestTags = async (doc_id) => {
    try {
      const res = await fetch(`/api/repository/${doc_id}/suggest-tags`, { method: "POST" });
      if (!res.ok) throw new Error("Failed");
      await loadData();
    } catch (e) {
      alert("Could not auto-tag: " + e.message);
    }
  };

  const handleTeamAction = async (memberEmail, action, extra = {}) => {
    try {
      const res = await fetch(`/api/team/members/${encodeURIComponent(memberEmail)}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action, ...extra }),
      });
      if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.detail || "Action failed"); }
      const team = await apiGet("/api/team");
      setTeamData(team);
    } catch (e) {
      alert(e.message);
    }
  };

  const mono = { fontFamily: "var(--mono)", fontSize: 12 };
  const prose = { fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif", fontSize: 13, lineHeight: 1.5 };

  const TAG_COLORS = {
    "competitive intelligence": { bg: "#EEF2FF", border: "#C7D2FE", text: "#4338CA" },
    "clinical data":            { bg: "#ECFDF5", border: "#6EE7B7", text: "#065F46" },
    "market research":          { bg: "#FFF7ED", border: "#FED7AA", text: "#9A3412" },
    "regulatory":               { bg: "#FEF2F2", border: "#FECACA", text: "#991B1B" },
    "internal strategy":        { bg: "#F0F9FF", border: "#BAE6FD", text: "#0C4A6E" },
    "financial":                { bg: "#FEFCE8", border: "#FDE68A", text: "#92400E" },
    "technical":                { bg: "#F0FDF4", border: "#BBF7D0", text: "#14532D" },
    "published literature":     { bg: "#FAF5FF", border: "#E9D5FF", text: "#6B21A8" },
    "presentations":            { bg: "#FFF1F2", border: "#FECDD3", text: "#9F1239" },
    "contracts":                { bg: "#F8FAFC", border: "#CBD5E1", text: "#334155" },
    "case studies":             { bg: "#FFFBEB", border: "#FCD34D", text: "#78350F" },
    "protocols & SOPs":         { bg: "#F0FDFA", border: "#99F6E4", text: "#134E4A" },
  };

  const tagStyle = (tag, active) => {
    const c = TAG_COLORS[tag];
    if (active) return { background: c ? c.text : "var(--ink)", color: "#fff", border: `1px solid ${c ? c.text : "var(--ink)"}` };
    return c ? { background: c.bg, border: `1px solid ${c.border}`, color: c.text } : { background: "var(--bg-2)", border: "1px solid var(--line-2)", color: "var(--ink-2)" };
  };

  const pill = (color, bg) => ({
    color, background: bg, fontSize: 11, fontWeight: 600,
    padding: "2px 7px", borderRadius: 4, display: "inline-block",
    fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
  });

  const statusBadge = (status) => {
    if (status === "indexed") return <span style={pill("#16a34a", "#dcfce7")}>Indexed</span>;
    if (status === "no_text") return <span title="Scanned or image-only — no text extracted" style={pill("#92400e", "#fef3c7")}>No text</span>;
    return <span style={pill("#6366f1", "#eef2ff")}>Processing</span>;
  };

  const fmtBytes = (n) => {
    if (!n && n !== 0) return "";
    if (n >= 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
    if (n >= 1024) return `${Math.round(n / 1024)} KB`;
    return `${n} B`;
  };

  // Self-explaining truncation pill — distinguishes the page cap from the chunk cap so a
  // dense doc that hit the chunk limit doesn't show a healthy page count next to a bare warning.
  const truncationPill = (doc) => {
    if (!doc.truncated) return null;
    const msg = doc.truncation_reason === "chunks"
      ? `Indexed first ${doc.chunk_count} passages (chunk limit reached) — some content not indexed`
      : `Indexed first ${doc.indexed_pages} of ${doc.total_pages} pages — remainder not indexed`;
    return <span title={msg} style={pill("#92400e", "#fef3c7")}>Truncated</span>;
  };

  const embedPill = (doc) => {
    if (doc.embed_status === "embedded" || doc.embed_status === undefined) return null;
    const msg = doc.embed_status === "pending"
      ? "Semantic embedding pending — using keyword (TF-IDF) search for this doc until re-indexed"
      : "Keyword (TF-IDF) search only — not semantically indexed";
    return <span title={msg} style={pill("#6366f1", "#eef2ff")}>Keyword only</span>;
  };

  if (loading) return (
    <div style={{ padding: "48px 64px", ...prose, color: "var(--ink-3)" }}>Loading repository…</div>
  );
  if (error) return (
    <div style={{ padding: "48px 64px", ...prose, color: "var(--rose)" }}>Error: {error}</div>
  );

  const renderActivitySidebar = () => {
    if (!isRepoAdmin) return null;

    return (
      <div style={{
        width: activityOpen ? 240 : 32,
        flexShrink: 0,
        transition: "width 0.2s ease",
        borderLeft: "1px solid var(--line-2)",
        paddingLeft: activityOpen ? 20 : 0,
        overflow: "hidden",
        position: "relative",
      }}>
        {activityOpen ? (
          <div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
            {isRepoAdmin && (
              <div>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
                  <span style={{ ...prose, fontWeight: 600, fontSize: 12, color: "var(--ink-3)", textTransform: "uppercase", letterSpacing: "0.05em" }}>Tips</span>
                  <button
                    onClick={() => setActivityOpen(false)}
                    title="Collapse"
                    style={{ background: "none", border: "none", cursor: "pointer", padding: 2, color: "var(--ink-4)", lineHeight: 1 }}
                  >
                    <Icon name="chevronRight" size={13} />
                  </button>
                </div>
                <div style={{ ...prose, fontSize: 12, color: "var(--ink-2)", lineHeight: 1.7 }}>
                  <div style={{ fontWeight: 600, marginBottom: 4 }}>Works well</div>
                  <ul style={{ margin: "0 0 10px", paddingLeft: 16 }}>
                    <li>PDFs and Word docs with selectable text</li>
                    <li>Research reports, competitive briefs, white papers</li>
                    <li>Meeting notes, memos, technical SOPs</li>
                  </ul>
                  <div style={{ fontWeight: 600, marginBottom: 4 }}>Avoid</div>
                  <ul style={{ margin: "0 0 10px", paddingLeft: 16 }}>
                    <li>Files with passwords or API keys</li>
                    <li>Scanned images without readable text</li>
                    <li>Spreadsheets, images, executables</li>
                  </ul>
                  <div style={{ fontWeight: 600, marginBottom: 4 }}>After upload</div>
                  <ul style={{ margin: 0, paddingLeft: 16 }}>
                    <li>Tags are applied automatically. You can refine or add more anytime.</li>
                    <li>Documents are available to agents immediately after processing completes</li>
                  </ul>
                </div>
              </div>
            )}
          </div>
        ) : (
          <button
            onClick={() => setActivityOpen(true)}
            title="Upload Tips"
            style={{
              background: "none", border: "none", cursor: "pointer", padding: "8px 0",
              display: "flex", flexDirection: "column", alignItems: "center", gap: 6,
              color: "var(--ink-3)", width: "100%",
            }}
          >
            <Icon name="chevronLeft" size={13} />
            <span style={{
              ...prose, fontSize: 10, fontWeight: 600, color: "var(--ink-4)",
              textTransform: "uppercase", letterSpacing: "0.08em",
              writingMode: "vertical-rl", transform: "rotate(180deg)",
            }}>Tips</span>
          </button>
        )}
      </div>
    );
  };

  const renderTagOverview = () => {
    const tagMap = {};
    documents.forEach(doc => {
      (doc.tags || []).forEach(tag => {
        if (!tagMap[tag]) tagMap[tag] = [];
        tagMap[tag].push(doc.title || doc.filename);
      });
    });
    const tags = Object.keys(tagMap).sort();
    if (!tags.length) return null;
    return (
      <div style={{ border: "1px solid var(--line-2)", borderRadius: 8, overflow: "hidden" }}>
        <button
          onClick={() => setTagOverviewOpen(v => !v)}
          style={{ width: "100%", padding: "10px 16px", display: "flex", justifyContent: "space-between", alignItems: "center", background: "none", border: "none", cursor: "pointer" }}
        >
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <span style={{ ...prose, fontWeight: 600, fontSize: 13, color: "var(--ink)" }}>Tags</span>
            <span style={{ ...prose, fontSize: 12, color: "var(--ink-3)" }}>{tags.length} {tags.length === 1 ? "tag" : "tags"} across {documents.filter(d => (d.tags || []).length > 0).length} documents</span>
          </div>
          <Icon name={tagOverviewOpen ? "chevronUp" : "chevronDown"} size={13} />
        </button>
        {tagOverviewOpen && (
          <div style={{ padding: "4px 16px 16px", display: "flex", flexDirection: "column", gap: 10 }}>
            {tags.map(tag => (
              <div key={tag}>
                <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }}>
                  <span style={{ ...mono, fontSize: 11, padding: "2px 8px", borderRadius: 10, ...tagStyle(tag, false) }}>{tag}</span>
                  <span style={{ ...prose, fontSize: 11, color: "var(--ink-4)" }}>{tagMap[tag].length}</span>
                </div>
                <div style={{ paddingLeft: 8, display: "flex", flexDirection: "column", gap: 2 }}>
                  {tagMap[tag].map((title, i) => (
                    <span key={i} style={{ ...prose, fontSize: 12, color: "var(--ink-2)" }}>· {title}</span>
                  ))}
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    );
  };

  const renderDocuments = () => (
    <div style={{ display: "flex", gap: 32, alignItems: "flex-start" }}>
      {/* Main column */}
      <div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 24 }}>

        {/* Repository summary */}
        {(summary || summaryUpdating) && (
          <div style={{ border: "1px solid var(--line-2)", borderRadius: 8, overflow: "hidden" }}>
            <button
              onClick={() => setSummaryOpen(v => !v)}
              style={{ width: "100%", padding: "12px 16px", display: "flex", justifyContent: "space-between", alignItems: "center", background: "var(--bg-2)", border: "none", cursor: "pointer" }}
            >
              <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                <span style={{ ...prose, fontWeight: 600, fontSize: 14, color: "var(--ink)" }}>Repository Summary</span>
                {summaryUpdating && (
                  <span style={{ ...prose, fontSize: 11, color: "var(--ink-3)", fontStyle: "italic" }}>updating…</span>
                )}
              </div>
              <Icon name={summaryOpen ? "chevronUp" : "chevronDown"} size={14} />
            </button>
            {summaryOpen && (
              <div style={{ padding: "16px", opacity: summaryUpdating ? 0.6 : 1 }}>
                {summaryUpdating && !summary && (
                  <div style={{ ...prose, color: "var(--ink-3)", fontStyle: "italic" }}>Generating summary…</div>
                )}
                {summary && renderSummaryMarkdown(summary)}
              </div>
            )}
          </div>
        )}

        {/* Tag Overview — live from document tags */}
        {renderTagOverview()}

        {/* Search */}
        <div>
          <div style={{ position: "relative" }}>
            <input
              type="text"
              value={searchQ}
              onChange={e => setSearchQ(e.target.value)}
              placeholder="Search document content…"
              style={{
                width: "100%", padding: "10px 12px", border: "1px solid var(--line-2)", borderRadius: 6,
                ...prose, background: "var(--bg)", color: "var(--ink)", boxSizing: "border-box", outline: "none",
              }}
            />
            {searchLoading && (
              <span style={{ position: "absolute", right: 10, top: "50%", transform: "translateY(-50%)", ...prose, color: "var(--ink-3)", fontSize: 12 }}>searching…</span>
            )}
          </div>
          {!searchQ.trim() && (
            <div style={{ marginTop: 4, ...prose, fontSize: 11, color: "var(--ink-4)" }}>Searches the text content of all indexed documents.</div>
          )}
          {searchError && (
            <div style={{ marginTop: 8, ...prose, fontSize: 12, color: "var(--rose)" }}>{searchError}</div>
          )}
          {searchResults.length > 0 && (
            <div style={{ marginTop: 8, border: "1px solid var(--line-2)", borderRadius: 6, overflow: "hidden" }}>
              {searchResults.map((r, i) => (
                <div key={i} style={{ padding: "10px 14px", borderBottom: i < searchResults.length - 1 ? "1px solid var(--line-2)" : "none", background: "var(--bg)" }}>
                  <div style={{ ...prose, fontWeight: 600, fontSize: 12, marginBottom: 3 }}>{r.doc_title} <span style={{ ...mono, fontWeight: 400, color: "var(--ink-3)" }}>· p.{r.page}</span></div>
                  <div style={{ ...mono, fontSize: 11, color: "var(--ink-2)", lineHeight: 1.55 }}>{r.excerpt}</div>
                </div>
              ))}
            </div>
          )}
          {searchQ.trim() && !searchLoading && !searchError && searchResults.length === 0 && (
            <div style={{ marginTop: 8, ...prose, color: "var(--ink-3)" }}>No results. Only documents with extractable text are searchable.</div>
          )}
        </div>

        {/* Tag Reference panel */}
        {standardTags.length > 0 && (
          <div style={{ border: "1px solid var(--line-2)", borderRadius: 8, overflow: "hidden" }}>
            <button
              onClick={() => setTagGuideOpen(v => !v)}
              style={{ width: "100%", padding: "10px 16px", display: "flex", justifyContent: "space-between", alignItems: "center", background: "none", border: "none", cursor: "pointer" }}
            >
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <span style={{ ...prose, fontWeight: 600, fontSize: 13, color: "var(--ink)" }}>Atlas Standard Tags</span>
                <span style={{ ...prose, fontSize: 12, color: "var(--ink-3)" }}>a shared vocabulary for your repository</span>
              </div>
              <Icon name={tagGuideOpen ? "chevronUp" : "chevronDown"} size={13} />
            </button>
            {tagGuideOpen && (
              <div style={{ padding: "4px 16px 16px", display: "flex", flexWrap: "wrap", gap: 6 }}>
                {standardTags.map(tag => (
                  <span key={tag} style={{ ...mono, fontSize: 11, padding: "3px 9px", borderRadius: 10, ...tagStyle(tag, false) }}>{tag}</span>
                ))}
                <div style={{ width: "100%", marginTop: 8, ...prose, fontSize: 12, color: "var(--ink-4)" }}>
                  These tags are suggested automatically when you upload documents. You can also add custom tags (anything outside this list).
                </div>
              </div>
            )}
          </div>
        )}

        {/* Tag filter bar */}
        {(() => {
          const allTags = [...new Set(documents.flatMap(d => d.tags || []))].sort();
          if (!allTags.length) return null;
          return (
            <div style={{ display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center" }}>
              <span style={{ ...prose, color: "var(--ink-3)", fontSize: 12, marginRight: 2 }}>Filter:</span>
              {allTags.map(tag => {
                const active = tagFilter.includes(tag);
                return (
                  <button key={tag} onClick={() => setTagFilter(prev => active ? prev.filter(t => t !== tag) : [...prev, tag])}
                    style={{ ...mono, fontSize: 11, padding: "3px 9px", borderRadius: 12, cursor: "pointer", fontWeight: active ? 600 : 400, ...tagStyle(tag, active) }}>
                    {tag}
                  </button>
                );
              })}
              {tagFilter.length > 0 && (
                <button onClick={() => setTagFilter([])} style={{ ...prose, fontSize: 12, color: "var(--ink-3)", background: "none", border: "none", cursor: "pointer", textDecoration: "underline" }}>clear</button>
              )}
            </div>
          );
        })()}

        {/* Document table */}
        {documents.length === 0 ? (
          <div style={{ padding: "32px 0", textAlign: "center", ...prose, color: "var(--ink-3)" }}>
            {isRepoAdmin
              ? "Upload your first document to get started."
              : "No documents yet. Ask your repository admin to add files."}
          </div>
        ) : (
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", borderCollapse: "collapse" }}>
              <thead>
                <tr style={{ borderBottom: "1px solid var(--line-2)" }}>
                  {["Filename", "Title", "Tags", "Status", "Uploaded by", "Date", ""].map(h => (
                    <th key={h} style={{ textAlign: "left", padding: "8px 10px", ...prose, fontSize: 12, color: "var(--ink-3)", fontWeight: 600, whiteSpace: "nowrap" }}>{h}</th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {documents.filter(doc => tagFilter.length === 0 || (doc.tags || []).some(t => tagFilter.includes(t))).map(doc => (
                  <tr key={doc.doc_id} style={{ borderBottom: "1px solid var(--line-2)" }}>
                    <td style={{ padding: "10px 10px", maxWidth: 160, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                      <a href={`/api/repository/${doc.doc_id}/download`} target="_blank" rel="noopener noreferrer" style={{ ...mono, fontSize: 12, color: "var(--ink-2)", textDecoration: "underline" }}>{doc.filename}</a>
                    </td>
                    <td style={{ padding: "10px 10px", maxWidth: 200 }}>
                      {isRepoAdmin && editingTitle[doc.doc_id] !== undefined ? (
                        <input
                          autoFocus
                          value={editingTitle[doc.doc_id]}
                          onChange={e => setEditingTitle(prev => ({ ...prev, [doc.doc_id]: e.target.value }))}
                          onBlur={() => handleTitleSave(doc.doc_id, editingTitle[doc.doc_id])}
                          onKeyDown={e => {
                            if (e.key === "Enter") handleTitleSave(doc.doc_id, editingTitle[doc.doc_id]);
                            if (e.key === "Escape") setEditingTitle(prev => { const n = { ...prev }; delete n[doc.doc_id]; return n; });
                          }}
                          style={{ width: "100%", ...prose, padding: "2px 4px", border: "1px solid var(--ink)", borderRadius: 3, background: "var(--bg)", color: "var(--ink)", outline: "none" }}
                        />
                      ) : (
                        <div style={{ display: "flex", alignItems: "center", gap: 5, minWidth: 0 }}>
                          <span style={{ ...prose, color: "var(--ink)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{doc.title}</span>
                          {isRepoAdmin && (
                            <button
                              onClick={() => setEditingTitle(prev => ({ ...prev, [doc.doc_id]: doc.title }))}
                              title="Edit title"
                              style={{ background: "none", border: "none", cursor: "pointer", color: "var(--ink-4)", padding: "1px 3px", borderRadius: 3, lineHeight: 1, fontSize: 12, flexShrink: 0, opacity: 0.6 }}
                            >✎</button>
                          )}
                        </div>
                      )}
                    </td>
                    <td style={{ padding: "10px 10px", minWidth: 160 }}>
                      <div style={{ display: "flex", flexWrap: "wrap", gap: 4, alignItems: "center" }}>
                        {(doc.tags || []).map(tag => (
                          <span key={tag} style={{ display: "inline-flex", alignItems: "center", gap: 3, ...mono, fontSize: 11, padding: "2px 7px", borderRadius: 10, ...tagStyle(tag, false) }}>
                            {tag}
                            {isRepoAdmin && (
                              <button onClick={() => handleRemoveTag(doc.doc_id, tag, doc.tags || [])}
                                style={{ background: "none", border: "none", cursor: "pointer", color: "inherit", padding: 0, lineHeight: 1, fontSize: 12, opacity: 0.6 }}>×</button>
                            )}
                          </span>
                        ))}
                        {isRepoAdmin && (doc.tags || []).length === 0 && editingTag[doc.doc_id] === undefined && (
                          <button onClick={() => handleSuggestTags(doc.doc_id)}
                            style={{ ...mono, fontSize: 11, color: "var(--ink-3)", background: "none", border: "1px dashed var(--line-2)", borderRadius: 10, cursor: "pointer", padding: "2px 7px" }}>Auto-tag</button>
                        )}
                        {isRepoAdmin && (
                          editingTag[doc.doc_id] !== undefined ? (
                            <div style={{ display: "flex", flexDirection: "column", gap: 5, minWidth: 180 }}>
                              <input
                                autoFocus
                                placeholder="tag…"
                                value={editingTag[doc.doc_id]}
                                onChange={e => setEditingTag(prev => ({ ...prev, [doc.doc_id]: e.target.value }))}
                                onBlur={() => handleAddTag(doc.doc_id, editingTag[doc.doc_id] || "", doc.tags || [])}
                                onKeyDown={e => {
                                  if (e.key === "Enter") handleAddTag(doc.doc_id, editingTag[doc.doc_id], doc.tags || []);
                                  if (e.key === "Escape") setEditingTag(prev => { const n = { ...prev }; delete n[doc.doc_id]; return n; });
                                }}
                                style={{ width: "100%", ...mono, fontSize: 11, padding: "3px 8px", border: "1px solid var(--ink)", borderRadius: 6, background: "var(--bg)", color: "var(--ink)", outline: "none", boxSizing: "border-box" }}
                              />
                              {(() => {
                                const existing = doc.tags || [];
                                const q = (editingTag[doc.doc_id] || "").toLowerCase();
                                const suggestions = standardTags.filter(t => !existing.includes(t) && (!q || t.includes(q)));
                                if (!suggestions.length) return null;
                                return (
                                  <div style={{ display: "flex", flexWrap: "wrap", gap: 3 }}>
                                    {suggestions.slice(0, 8).map(s => (
                                      <button key={s} onMouseDown={e => { e.preventDefault(); handleAddTag(doc.doc_id, s, existing); }}
                                        style={{ ...mono, fontSize: 10, padding: "2px 7px", borderRadius: 10, cursor: "pointer", border: "none", ...tagStyle(s, false) }}>
                                        {s}
                                      </button>
                                    ))}
                                  </div>
                                );
                              })()}
                            </div>
                          ) : (
                            <button onClick={() => setEditingTag(prev => ({ ...prev, [doc.doc_id]: "" }))}
                              style={{ ...mono, fontSize: 11, color: "var(--ink-3)", background: "none", border: "1px dashed var(--line-2)", borderRadius: 10, cursor: "pointer", padding: "2px 7px" }}>+ tag</button>
                          )
                        )}
                      </div>
                    </td>
                    <td style={{ padding: "10px 10px", whiteSpace: "nowrap" }}>
                      <div style={{ display: "flex", flexDirection: "column", gap: 4, alignItems: "flex-start" }}>
                        <div style={{ display: "flex", flexWrap: "wrap", gap: 4, alignItems: "center" }}>
                          {statusBadge(doc.status)}
                          {truncationPill(doc)}
                          {embedPill(doc)}
                        </div>
                        {doc.status === "indexed" && (
                          <span style={{ ...mono, fontSize: 10, color: "var(--ink-4)" }}>
                            {doc.total_pages ? `${doc.indexed_pages} of ${doc.total_pages} pages · ` : ""}
                            {doc.chunk_count != null ? `${doc.chunk_count} passages` : ""}
                            {doc.size_bytes ? ` · ${fmtBytes(doc.size_bytes)}` : ""}
                          </span>
                        )}
                      </div>
                    </td>
                    <td style={{ padding: "10px 10px", whiteSpace: "nowrap" }}>
                      <span style={{ ...prose, fontSize: 12, color: "var(--ink-3)" }}>{doc.uploaded_by.split("@")[0]}</span>
                    </td>
                    <td style={{ padding: "10px 10px", whiteSpace: "nowrap" }}>
                      <span style={{ ...mono, fontSize: 11, color: "var(--ink-3)" }}>{(doc.uploaded_at || "").slice(0, 10)}</span>
                    </td>
                    <td style={{ padding: "10px 10px", whiteSpace: "nowrap" }}>
                      {isRepoAdmin && (
                        <button
                          onClick={() => handleDelete(doc.doc_id)}
                          style={{ background: "none", border: "none", color: "var(--rose)", cursor: "pointer", ...prose, fontSize: 12, padding: 0 }}
                        >Delete</button>
                      )}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}

        {/* Upload zone — repo admin only, at the bottom */}
        {isRepoAdmin && (
          <div style={{ marginTop: 8 }}>
            <div
              onDragOver={e => { e.preventDefault(); setUploadDragging(true); }}
              onDragLeave={() => setUploadDragging(false)}
              onDrop={handleDrop}
              onClick={() => fileInputRef.current?.click()}
              style={{
                border: `2px dashed ${uploadDragging ? "var(--ink)" : "var(--line-2)"}`,
                borderRadius: 8, padding: 24, textAlign: "center", cursor: "pointer",
                background: uploadDragging ? "var(--bg-2)" : "transparent",
                transition: "border-color 0.15s, background 0.15s",
              }}
            >
              <input
                ref={fileInputRef} type="file" accept=".pdf,.docx,.txt,.md"
                style={{ display: "none" }}
                onChange={e => { const f = e.target.files[0]; if (f) handleUploadFile(f); e.target.value = ""; }}
              />
              {uploading ? (
                <span style={{ ...prose, color: "var(--ink-3)" }}>Uploading…</span>
              ) : (
                <div>
                  <div style={{ ...prose, fontWeight: 600, fontSize: 14, marginBottom: 4 }}>Drop a file or click to upload</div>
                  <div style={{ ...prose, color: "var(--ink-3)", fontSize: 12 }}>PDF, Word, TXT, Markdown · PDF/Word up to 25 MB · Text up to 5 MB</div>
                </div>
              )}
            </div>
            {uploadError && (
              <div style={{ marginTop: 8, ...prose, color: "var(--rose)" }}>{uploadError}</div>
            )}
          </div>
        )}
      </div>

      {/* Activity sidebar */}
      {tab === "documents" && renderActivitySidebar()}
    </div>
  );

  const renderTeam = () => {
    if (!teamData) return <div style={{ ...prose, color: "var(--ink-3)" }}>Loading team…</div>;

    const ROLE_LABELS = { member: "Member", repo_admin: "Repo Admin", org_admin: "Org Admin" };

    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 32 }}>

        {isOrgAdmin && (
          <div>
            <div style={{ ...prose, fontWeight: 600, fontSize: 14, marginBottom: 12, color: "var(--ink)" }}>Pending Requests</div>
            {(!teamData.pending_requests || teamData.pending_requests.length === 0) ? (
              <div style={{ ...prose, color: "var(--ink-3)" }}>No pending join requests.</div>
            ) : (
              teamData.pending_requests.map(r => (
                <div key={r.email} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: "1px solid var(--line-2)" }}>
                  <span style={{ ...prose, flex: 1 }}>{r.name ? `${r.name} (${r.email})` : r.email}</span>
                  <span style={{ ...mono, color: "var(--ink-3)", fontSize: 11 }}>{(r.requested_at || "").slice(0, 10)}</span>
                  <select
                    value={approveRoles[r.email] || "member"}
                    onChange={e => setApproveRoles(prev => ({ ...prev, [r.email]: e.target.value }))}
                    style={{ ...prose, padding: "3px 6px", border: "1px solid var(--line-2)", borderRadius: 4, background: "var(--bg)", color: "var(--ink)" }}
                  >
                    <option value="member">Member</option>
                    <option value="repo_admin">Repo Admin</option>
                    <option value="org_admin">Org Admin</option>
                  </select>
                  <button
                    onClick={() => handleTeamAction(r.email, "approve", { role: approveRoles[r.email] || "member" })}
                    style={{ ...prose, padding: "4px 12px", background: "var(--ink)", color: "var(--bg)", border: "none", borderRadius: 4, cursor: "pointer", fontWeight: 500 }}
                  >Approve</button>
                  <button
                    onClick={() => { if (confirm(`Deny join request from ${r.email}?`)) handleTeamAction(r.email, "deny"); }}
                    style={{ ...prose, padding: "4px 12px", background: "none", color: "var(--rose)", border: "1px solid var(--rose)", borderRadius: 4, cursor: "pointer" }}
                  >Deny</button>
                </div>
              ))
            )}
          </div>
        )}

        {isOrgAdmin && teamData.denied_requests?.length > 0 && (
          <div>
            <div style={{ ...prose, fontWeight: 600, fontSize: 14, marginBottom: 12, color: "var(--ink)" }}>Denied Requests</div>
            {teamData.denied_requests.map(r => (
              <div key={r.email} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: "1px solid var(--line-2)" }}>
                <span style={{ ...prose, flex: 1, color: "var(--ink-3)" }}>{r.name ? `${r.name} (${r.email})` : r.email}</span>
                <span style={{ ...mono, color: "var(--ink-3)", fontSize: 11 }}>denied {(r.denied_at || "").slice(0, 10)}</span>
                <button
                  onClick={() => { if (confirm(`Allow ${r.email} to request to join again?`)) handleTeamAction(r.email, "clear_denial"); }}
                  style={{ ...prose, padding: "4px 12px", background: "none", color: "var(--ink)", border: "1px solid var(--line-2)", borderRadius: 4, cursor: "pointer" }}
                >Re-allow</button>
              </div>
            ))}
          </div>
        )}

        <div>
          <div style={{ ...prose, fontWeight: 600, fontSize: 14, marginBottom: 12, color: "var(--ink)" }}>Members</div>
          {(teamData.members || []).map(m => (
            <div key={m.email} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: "1px solid var(--line-2)" }}>
              <span style={{ ...prose, flex: 1 }}>{m.name ? `${m.name} (${m.email})` : m.email}</span>
              <span style={{ ...mono, color: "var(--ink-3)", fontSize: 11 }}>{(m.joined_at || "").slice(0, 10)}</span>
              {isOrgAdmin && m.email !== user?.email ? (
                <>
                  <select
                    value={m.role}
                    onChange={e => {
                      const newRole = e.target.value;
                      if (!confirm(`Change ${m.email}'s role to ${newRole}?`)) {
                        e.target.value = m.role;
                        return;
                      }
                      handleTeamAction(m.email, "change_role", { role: newRole });
                    }}
                    style={{ ...prose, padding: "3px 6px", border: "1px solid var(--line-2)", borderRadius: 4, background: "var(--bg)", color: "var(--ink)" }}
                  >
                    <option value="member">Member</option>
                    <option value="repo_admin">Repo Admin</option>
                    <option value="org_admin">Org Admin</option>
                  </select>
                  <button
                    onClick={() => { if (confirm(`Remove ${m.email} from the org?`)) handleTeamAction(m.email, "remove"); }}
                    style={{ ...prose, padding: "4px 10px", background: "none", color: "var(--rose)", border: "1px solid var(--rose)", borderRadius: 4, cursor: "pointer" }}
                  >Remove</button>
                </>
              ) : (
                <span style={{ ...prose, fontSize: 12, padding: "3px 8px", background: "var(--bg-2)", borderRadius: 4, color: "var(--ink-3)" }}>{ROLE_LABELS[m.role] || m.role}</span>
              )}
            </div>
          ))}
        </div>

        {isOrgAdmin && (
          <div>
            <div style={{ ...prose, fontWeight: 600, fontSize: 14, marginBottom: 12, color: "var(--ink)" }}>Invite Member</div>
            <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
              <input
                type="email"
                placeholder={`colleague@${teamData.org_domain || "yourcompany.com"}`}
                value={inviteEmail}
                onChange={e => { setInviteEmail(e.target.value); setInviteStatus(null); setInviteError(null); }}
                style={{ ...prose, flex: 1, padding: "6px 10px", border: "1px solid var(--line-2)", borderRadius: 4, background: "var(--bg)", color: "var(--ink)", outline: "none" }}
              />
              <select
                value={inviteRole}
                onChange={e => setInviteRole(e.target.value)}
                style={{ ...prose, padding: "6px 8px", border: "1px solid var(--line-2)", borderRadius: 4, background: "var(--bg)", color: "var(--ink)" }}
              >
                <option value="member">Member</option>
                <option value="repo_admin">Repo Admin</option>
                <option value="org_admin">Org Admin</option>
              </select>
              <button
                disabled={!inviteEmail.trim() || inviteStatus === "sending"}
                onClick={async () => {
                  setInviteStatus("sending");
                  setInviteError(null);
                  try {
                    const res = await fetch("/api/team/invite", {
                      method: "POST",
                      headers: { "Content-Type": "application/json" },
                      body: JSON.stringify({ email: inviteEmail.trim(), role: inviteRole }),
                    });
                    if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.detail || "Invite failed"); }
                    setInviteStatus("sent");
                    setInviteEmail("");
                    setInviteRole("member");
                    const team = await apiGet("/api/team");
                    setTeamData(team);
                  } catch (e) {
                    setInviteStatus("error");
                    setInviteError(e.message);
                  }
                }}
                style={{ ...prose, padding: "6px 14px", background: "var(--ink)", color: "var(--bg)", border: "none", borderRadius: 4, cursor: "pointer", fontWeight: 500, opacity: (!inviteEmail.trim() || inviteStatus === "sending") ? 0.5 : 1 }}
              >{inviteStatus === "sending" ? "Inviting…" : "Invite"}</button>
            </div>
            {inviteStatus === "sent" && <div style={{ marginTop: 6, ...prose, fontSize: 12, color: "var(--ink-3)" }}>Invited successfully — they'll have access on next login.</div>}
            {inviteStatus === "error" && <div style={{ marginTop: 6, ...prose, fontSize: 12, color: "var(--rose)" }}>{inviteError}</div>}
          </div>
        )}

        <div style={{ marginTop: 24 }}>
          {(() => {
            const isOnlyOrgAdmin = isOrgAdmin &&
              (teamData?.members || []).filter(m => m.role === "org_admin").length === 1;
            return (
              <>
                {isOnlyOrgAdmin && (
                  <div style={{ ...prose, fontSize: 12, color: "var(--ink-3)", marginBottom: 8 }}>
                    You are the only org admin. Assign another org admin before leaving.
                  </div>
                )}
                <button
                  disabled={isOnlyOrgAdmin}
                  onClick={async () => {
                    if (!confirm("Leave this organization? You will lose access to the repository.")) return;
                    try {
                      await handleTeamAction(user.email, "leave");
                      window.location.reload();
                    } catch(e) { alert(e.message || "Could not leave org."); }
                  }}
                  style={{
                    fontFamily: "var(--mono)", fontSize: 12, padding: "6px 14px",
                    background: "none", color: "var(--rose)", border: "1px solid var(--rose)",
                    borderRadius: 4, cursor: isOnlyOrgAdmin ? "not-allowed" : "pointer",
                    opacity: isOnlyOrgAdmin ? 0.4 : 1,
                  }}
                >Leave org</button>
              </>
            );
          })()}
        </div>
      </div>
    );
  };

  return (
    <div style={{ maxWidth: 1060, margin: "0 auto", padding: "0 48px 64px" }}>
      {/* Tabs */}
      <div style={{ display: "flex", borderBottom: "1px solid var(--line-2)", marginBottom: 32 }}>
        {(user?.solo_repo ? ["documents"] : ["documents", "team"]).map(t => (
          <button
            key={t}
            onClick={() => setTab(t)}
            style={{
              padding: "10px 20px", background: "none", border: "none",
              borderBottom: tab === t ? "2px solid var(--ink)" : "2px solid transparent",
              marginBottom: -1, cursor: "pointer",
              ...prose, fontSize: 13,
              fontWeight: tab === t ? 600 : 400,
              color: tab === t ? "var(--ink)" : "var(--ink-3)",
              textTransform: "capitalize",
            }}
          >
            {t === "documents"
              ? `Documents (${documents.length})`
              : isOrgAdmin && teamData?.pending_requests?.length > 0
                ? `Team (${teamData.pending_requests.length})`
                : "Team"}
          </button>
        ))}
      </div>

      {tab === "documents" ? renderDocuments() : renderTeam()}
    </div>
  );
}
