// Workspace: project list, project detail (folder tree + file list)

// Format ISO timestamp as a relative string ("2 hr ago", "Yesterday", etc.)
const relativeTime = (iso) => {
  if (!iso) return "—";
  const d = new Date(iso);
  const now = Date.now();
  const diff = Math.floor((now - d.getTime()) / 1000);
  if (diff < 3600) return `${Math.max(1, Math.floor(diff / 60))} min ago`;
  if (diff < 86400) return `${Math.floor(diff / 3600)} hr ago`;
  if (diff < 172800) return "Yesterday";
  if (diff < 604800) return `${Math.floor(diff / 86400)} d ago`;
  return d.toLocaleDateString("en-IN", { day: "numeric", month: "short" });
};

// Convert API artifact to display-friendly shape
const toDisplayFile = (a) => ({
  id: a.id,
  name: a.file_name,
  ext: (a.file_type || "").toLowerCase(),
  size: "—",
  type: a.doc_type,
  conf: a.confidentiality,
  owner: "—",
  owner_id: a.owner_id,
  added: relativeTime(a.uploaded_at),
  uploaded_at: a.uploaded_at,
  governance: [
    a.reusable && "Reusable",
    a.best_in_class && "Best-in-Class",
    a.manager_approved && "Manager Approved",
  ].filter(Boolean),
  raw: a,
});

// ─── File action menu (Download · Delete · Governance) ───
const FileActionMenu = ({ file, currentUser, tier, onRefresh }) => {
  const [open, setOpen] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const ref = React.useRef(null);

  // Close menu on outside click
  React.useEffect(() => {
    if (!open) return;
    const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [open]);

  const isLeadership = tier === "leadership";
  const isOwner = currentUser && file.raw.owner_id === currentUser.id;
  const managerDesignations = ["Manager", "Associate Director", "Director", "Partner"];
  const isManagerPlus = currentUser && managerDesignations.includes(currentUser.designation);

  // Delete: leadership always, execution only own file within 24h
  const canDelete = (() => {
    if (isLeadership) return true;
    if (!isOwner) return false;
    if (!file.uploaded_at) return false;
    const age = Date.now() - new Date(file.uploaded_at).getTime();
    return age <= 24 * 60 * 60 * 1000;
  })();

  const handleDownload = async () => {
    setBusy(true);
    try {
      const resp = await PrimaAPI.getDownloadUrl(file.id);
      window.open(resp.download_url, "_blank");
    } catch (e) {
      alert(e.message || "Download failed.");
    } finally {
      setBusy(false); setOpen(false);
    }
  };

  const handleDelete = async () => {
    if (!confirm(`Delete "${file.name}"? This cannot be undone.`)) return;
    setBusy(true);
    try {
      await PrimaAPI.deleteArtifact(file.id);
      onRefresh();
    } catch (e) {
      alert(e.message || "Delete failed.");
    } finally {
      setBusy(false); setOpen(false);
    }
  };

  const toggleGovernance = async (flag) => {
    setBusy(true);
    try {
      const payload = {};
      payload[flag] = !file.raw[flag];
      await PrimaAPI.updateGovernance(file.id, payload);
      onRefresh();
    } catch (e) {
      alert(e.message || "Update failed.");
    } finally {
      setBusy(false); setOpen(false);
    }
  };

  return (
    <div ref={ref} style={{ position: "relative" }}>
      <button className="icon-btn" onClick={() => setOpen(o => !o)} disabled={busy}>
        <Icon name="more" size={14} />
      </button>
      {open && (
        <div className="action-menu" style={{
          position: "absolute", right: 0, top: "100%", zIndex: 100,
          minWidth: 200, background: "var(--bg-elev)", border: "1px solid var(--hairline)",
          borderRadius: "var(--r-input, 6px)", boxShadow: "0 8px 24px rgba(0,0,0,.12)",
          padding: "4px 0", fontSize: 13,
        }}>
          {/* Download */}
          <button style={menuItemStyle} onClick={handleDownload} disabled={busy}>
            <Icon name="download" size={13} /> Download file
          </button>

          {/* Governance toggles */}
          <div style={{ borderTop: "1px solid var(--hairline)", margin: "4px 0" }} />
          <div style={{ padding: "4px 12px", fontSize: 10.5, color: "var(--fg-subtle)", textTransform: "uppercase", letterSpacing: 0.5 }}>Governance</div>

          <button style={menuItemStyle} onClick={() => toggleGovernance("reusable")} disabled={busy}>
            {file.raw.reusable ? <Icon name="check" size={11} /> : <span style={{ width: 11, display: "inline-block" }} />}
            {" "}Reusable
          </button>
          <button style={menuItemStyle} onClick={() => toggleGovernance("best_in_class")} disabled={busy || (!isManagerPlus && !isLeadership)}>
            {file.raw.best_in_class ? <Icon name="check" size={11} /> : <span style={{ width: 11, display: "inline-block" }} />}
            {" "}Best-in-Class
            {!isManagerPlus && !isLeadership && <span style={{ marginLeft: "auto", fontSize: 10, color: "var(--fg-subtle)" }}>Manager+</span>}
          </button>
          <button style={menuItemStyle} onClick={() => toggleGovernance("manager_approved")} disabled={busy || (!isManagerPlus && !isLeadership)}>
            {file.raw.manager_approved ? <Icon name="check" size={11} /> : <span style={{ width: 11, display: "inline-block" }} />}
            {" "}Manager Approved
            {!isManagerPlus && !isLeadership && <span style={{ marginLeft: "auto", fontSize: 10, color: "var(--fg-subtle)" }}>Manager+</span>}
          </button>

          {/* Delete */}
          {canDelete && (
            <>
              <div style={{ borderTop: "1px solid var(--hairline)", margin: "4px 0" }} />
              <button style={{ ...menuItemStyle, color: "var(--err, #dc2626)" }} onClick={handleDelete} disabled={busy}>
                <Icon name="trash" size={13} /> Delete artifact
              </button>
            </>
          )}
        </div>
      )}
    </div>
  );
};

const menuItemStyle = {
  display: "flex", alignItems: "center", gap: 8, width: "100%",
  padding: "7px 12px", border: 0, background: "transparent",
  color: "inherit", textAlign: "left", fontSize: 13, cursor: "pointer",
  borderRadius: 0,
};

const Sidebar = ({ active, onNav, onOpenProfile, onLogout, tier, collapsed, onToggleCollapse, currentUser }) => {
  const items = [
    { id: "workspace", label: "Project Workspace", icon: "folder" },
    { id: "research", label: "Research Hub", icon: "book" },
    { id: "thought", label: "Thought Leadership", icon: "mic" },
    { id: "leaderboard", label: "Leaderboard", icon: "trophy" },
    { id: "profile", label: "Update Profile", icon: "user" },
  ];
  const displayName = currentUser?.full_name || "—";
  const displayRole = tier ? tier.toUpperCase() : "USER";

  return (
    <aside className="sidebar">
      <button className="sidebar-toggle" onClick={onToggleCollapse} aria-label="Toggle sidebar">
        <Icon name="chevron-left" size={12} />
      </button>
      <div className="brand">
        <span className="brand-mark">Prima<em>·</em>Curator</span>
        <span className="brand-tag">v1.0</span>
      </div>
      <nav className="nav">
        <div className="nav-section">Library</div>
        {items.map(it => (
          <button key={it.id} className="nav-item" data-active={active === it.id}
                  data-tip={it.label}
                  onClick={() => onNav(it.id)}>
            <Icon name={it.icon} size={15} />
            <span>{it.label}</span>
          </button>
        ))}

        <div className="nav-section">Quick</div>
        <button className="nav-item" data-tip="New project" onClick={() => onNav("workspace", { create: true })}>
          <Icon name="plus" size={15} /> <span>New project</span>
        </button>
      </nav>
      <div className="user-card" style={{ cursor: "pointer", display: "flex", alignItems: "center" }}>
        <div style={{ display: "flex", gap: 10, alignItems: "center", flex: 1, minWidth: 0 }} onClick={onOpenProfile}>
          <Avatar name={displayName} />
          <div className="meta">
            <div className="name">{displayName}</div>
            <div className="role">{displayRole}</div>
          </div>
        </div>
        <button className="icon-btn" style={{ marginLeft: "auto", color: "var(--fg-muted)" }} onClick={(e) => { e.stopPropagation(); onLogout?.(); }} title="Log out">
          <Icon name="log-out" size={14} />
        </button>
      </div>
    </aside>
  );
};

// ─── Notification bell ───
const NotificationBell = ({ currentUser }) => {
  const [open, setOpen] = React.useState(false);
  const [notifs, setNotifs] = React.useState([]);
  const [unread, setUnread] = React.useState(0);
  const ref = React.useRef(null);

  const load = () => {
    PrimaAPI.getNotifications()
      .then(data => { setNotifs(data.items || []); setUnread(data.unread_count || 0); })
      .catch(() => {});
  };

  React.useEffect(() => {
    if (!currentUser) return;
    load();
    const id = setInterval(load, 30000);
    return () => clearInterval(id);
  }, [currentUser]);

  React.useEffect(() => {
    if (!open) return;
    const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [open]);

  const handleOpen = () => { setOpen(o => !o); if (!open) load(); };

  const handleMarkRead = async () => {
    await PrimaAPI.markAllRead().catch(() => {});
    setNotifs(ns => ns.map(n => ({ ...n, is_read: true })));
    setUnread(0);
  };

  return (
    <div className="notif-bell" ref={ref}>
      <button className="notif-btn" onClick={handleOpen} title="Notifications">
        <Icon name="bell" size={17} />
        {unread > 0 && <span className="notif-badge">{unread > 9 ? "9+" : unread}</span>}
      </button>
      {open && (
        <div className="notif-dropdown">
          <div className="notif-header">
            <span className="notif-title">Notifications</span>
            {unread > 0 && (
              <button className="notif-mark-read" onClick={handleMarkRead}>Mark all read</button>
            )}
          </div>
          <div className="notif-list">
            {notifs.length === 0
              ? <div className="notif-empty">No notifications</div>
              : notifs.map(n => (
                  <div key={n.id} className={`notif-item${n.is_read ? "" : " unread"}`}>
                    <div className="notif-msg">{n.message}</div>
                    <div className="notif-time">{relativeTime(n.created_at)}</div>
                  </div>
                ))
            }
          </div>
        </div>
      )}
    </div>
  );
};

const Topbar = ({ crumb, action, onSearch, currentUser }) => (
  <div className="topbar">
    <div className="crumb">
      {crumb.map((c, i) => (
        <React.Fragment key={i}>
          {i > 0 && <span className="sep"><Icon name="chevron-right" size={11} /></span>}
          <span className={i === crumb.length - 1 ? "here" : ""}>{c}</span>
        </React.Fragment>
      ))}
    </div>
    <div className="search-bar" onClick={onSearch}>
      <Icon name="search" size={13} />
      <span>Search projects, artifacts, people…</span>
      <kbd>⌘K</kbd>
    </div>
    <NotificationBell currentUser={currentUser} />
    {action}
  </div>
);

const ProjectStatusTag = ({ project, canEdit, onRefresh }) => {
  const [busy, setBusy] = React.useState(false);
  
  if (!canEdit) {
    return (
      <span className={"tag " + (project.status === "active" ? "ok" : project.status === "review" ? "warn" : "")}>
        {project.status}
      </span>
    );
  }

  return (
    <select
      value={project.status}
      disabled={busy}
      onClick={(e) => e.stopPropagation()}
      onChange={async (e) => {
        e.stopPropagation();
        const newStatus = e.target.value;
        setBusy(true);
        try {
          if (newStatus === "closed") {
            const reason = window.prompt("Closing project — please capture closure reason or final outcome:");
            if (reason === null) { setBusy(false); return; } // Cancelled
            // We could send this reason to the backend if we had a field for it.
            // For now, we'll just log it or we can add a 'closure_note' to the API call.
            await PrimaAPI.updateProjectStatus(project.id, newStatus); 
          } else {
            await PrimaAPI.updateProjectStatus(project.id, newStatus);
          }
          if (onRefresh) onRefresh();
        } catch (err) {
          alert(err.message || "Failed to update status");
        } finally {
          setBusy(false);
        }
      }}
      className={"tag " + (project.status === "active" ? "ok" : project.status === "review" ? "warn" : "")}
      style={{ border: "none", cursor: "pointer", fontWeight: 600, textTransform: "capitalize", paddingRight: 18 }}
    >
      {["active", "review", "closed"].map(s => (
        <option key={s} value={s}>{s}</option>
      ))}
    </select>
  );
};

// ─── Contribution rank strip ───
const RankStrip = ({ currentUser, onViewLeaderboard }) => {
  const [entries, setEntries] = React.useState(null);

  React.useEffect(() => {
    PrimaAPI.getLeaderboard(20)
      .then(res => setEntries(res.entries || []))
      .catch(() => {});
  }, []);

  if (!entries || !currentUser) return null;
  const me = entries.find(e => e.user_id === currentUser.id);
  if (!me) return null;

  const top    = entries[0];
  const next   = entries.find(e => e.rank === me.rank - 1);
  const below  = entries.find(e => e.rank === me.rank + 1);
  const gapToNext = next ? next.score - me.score : 0;
  const pctOfTop  = top.score > 0 ? Math.round((me.score / top.score) * 100) : 100;

  const tier      = me.rank === 1 ? "Gold" : me.rank <= 3 ? "Silver" : me.rank <= 6 ? "Bronze" : "Rising";
  const tierColor = me.rank === 1 ? "#d4a017" : me.rank <= 3 ? "#9aa0a6" : me.rank <= 6 ? "#cd7f32" : "var(--fg-subtle)";

  return (
    <div className="rank-strip">
      <div className="rs-medal">
        <span className="rs-num">#{me.rank}</span>
        <span className="rs-of">of {entries.length}</span>
      </div>

      <div className="rs-main">
        <div className="rs-row">
          <div className="rs-eyebrow">
            <Icon name="trophy" size={10} /> Your contribution rank
            <span className="rs-tier" style={{ color: tierColor, borderColor: tierColor }}>{tier}</span>
          </div>
        </div>
        <div className="rs-headline">
          {next
            ? <>You're <em>{gapToNext} points</em> behind <strong>{next.full_name}</strong> for #{me.rank - 1}.</>
            : <>You're at the top — keep contributing to hold the lead.</>}
        </div>
        <div className="rs-progress">
          <div className="rs-bar">
            <div className="rs-fill" style={{ width: `${pctOfTop}%` }} />
            <div className="rs-marker" style={{ left: `${pctOfTop}%` }}>
              <span className="rs-marker-dot" />
            </div>
          </div>
          <div className="rs-scale">
            <span>{me.score} pts</span>
            <span>{top.score} (#1)</span>
          </div>
        </div>
      </div>

      <div className="rs-cta">
        <button className="btn ghost sm" onClick={onViewLeaderboard}>
          View leaderboard <Icon name="chevron-right" size={12} />
        </button>
        {below && (
          <div className="rs-pursued">
            ⤵ <strong>{below.full_name.split(" ").slice(-1)[0]}</strong> is {me.score - below.score} pts behind you
          </div>
        )}
      </div>
    </div>
  );
};

// ─── Project list ───
const ProjectWorkspace = ({ onOpen, onCreate, onNav, currentUser, tier, showRbac, refreshKey }) => {
  const [projects, setProjects] = React.useState([]);
  const [stats, setStats] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [filter, setFilter] = React.useState("all"); // role filter: all, owner, member
  const [statusFilter, setStatusFilter] = React.useState("all"); // status filter: all, active, closed
  const [showStatusMenu, setShowStatusMenu] = React.useState(false);

  const refreshLocal = React.useCallback(() => {
    setLoading(true);
    Promise.all([
      PrimaAPI.listProjects(),
      PrimaAPI.getProjectStats()
    ]).then(([projData, statData]) => {
      setProjects(projData);
      setStats(statData);
    })
      .catch(() => { setProjects([]); setStats(null); })
      .finally(() => setLoading(false));
  }, []);

  React.useEffect(() => {
    refreshLocal();
  }, [refreshLocal, refreshKey]);

  const filtered = projects.filter(p => {
    const roleMatch = filter === "all" || p.membership === filter;
    const statusMatch = statusFilter === "all" || p.status === statusFilter;
    return roleMatch && statusMatch;
  });

  // Derive membership label from API field
  const membershipLabel = (p) => {
    if (tier === "leadership") return "overseer";
    if (!p.membership) return "none";
    return p.membership; // "owner" | "member"
  };

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <div className="eyebrow"><Icon name="folder" size={11} /> Workspace</div>
          <h1 className="page-title">Project <em>Workspace</em></h1>
          <p className="page-sub">A living index of every active engagement. Each project auto-generates a six-folder file system the moment it's created.</p>
        </div>
        <div style={{ display: "flex", gap: 8, position: "relative" }}>
          <button className="btn" onClick={() => setShowStatusMenu(!showStatusMenu)}>
            <Icon name="filter" size={13} /> Filters {statusFilter !== "all" ? `(${statusFilter})` : ""}
          </button>
          {showStatusMenu && (
            <div className="card" style={{ position: "absolute", top: "100%", right: 110, zIndex: 10, width: 140, marginTop: 4, padding: 4, boxShadow: "0 4px 12px rgba(0,0,0,0.1)" }}>
              {["all", "active", "closed"].map(s => (
                <button key={s} 
                        className="btn sm ghost" 
                        style={{ width: "100%", justifyContent: "flex-start", background: statusFilter === s ? "var(--bg-tint)" : "transparent" }}
                        onClick={() => { setStatusFilter(s); setShowStatusMenu(false); }}>
                  {s === "all" ? "All Status" : s[0].toUpperCase() + s.slice(1)}
                </button>
              ))}
            </div>
          )}
          <button className="btn primary" onClick={onCreate}><Icon name="plus" size={14} /> Create project</button>
        </div>
      </div>

      <RankStrip currentUser={currentUser} onViewLeaderboard={() => onNav && onNav("leaderboard")} />

      <div className="grid-4" style={{ marginBottom: 20 }}>
        <div className="metric">
          <div className="label">Total Projects</div>
          <div className="value">{stats?.total_projects || 0}</div>
          <div className="delta">Unique Projects</div>
        </div>
        <div className="metric">
          <div className="label">Projects</div>
          <div className="value">{stats?.my_projects || 0}</div>
          <div className="delta">Owned by me</div>
        </div>
        <div className="metric">
          <div className="label">Member In</div>
          <div className="value">{stats?.member_projects || 0}</div>
          <div className="delta">Team projects</div>
        </div>
        <div className="metric">
          <div className="label">Artifacts</div>
          <div className="value">{stats?.artifacts_uploaded || 0}</div>
          <div className="delta">Uploaded by me</div>
        </div>
      </div>

      <div className="filter-rail">
        <div className="pill-group">
          {[
            { id: "all", label: tier === "leadership" ? "All projects" : "All my projects" },
            { id: "owner", label: "Owner" },
            { id: "member", label: "Member" }
          ].map(f => (
            <button key={f.id} data-active={filter === f.id} onClick={() => setFilter(f.id)}>
              {f.label}
            </button>
          ))}
        </div>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--fg-subtle)" }}>
          {filtered.length} project{filtered.length === 1 ? "" : "s"}
        </span>
      </div>

      {loading ? (
        <div className="empty" style={{ marginTop: 24 }}>
          <div style={{ color: "var(--fg-muted)", fontFamily: "var(--font-mono)", fontSize: 13 }}>Loading projects…</div>
        </div>
      ) : (
        <div className="proj-grid" style={{ marginTop: 16 }}>
          {filtered.map(p => {
            const mem = membershipLabel(p);
            return (
              <button key={p.id} className="proj-card" onClick={() => onOpen(p, p.membership || null)}>
                <div className="row">
                  <span className="tag">{p.code}</span>
                  <span style={{ marginLeft: "auto", display: "flex", gap: 4 }}>
                    {showRbac && mem === "owner" && <span className="tag accent"><Icon name="shield" size={9} /> Owner</span>}
                    {showRbac && mem === "member" && <span className="tag info">Member</span>}
                    {showRbac && mem === "overseer" && <span className="tag warn">Overseer</span>}
                    {showRbac && !p.membership && p.has_pending_request && <span className="tag warn"><Icon name="clock" size={9} /> Pending</span>}
                    <ProjectStatusTag 
                      project={p} 
                      canEdit={tier === "leadership" || mem === "owner"} 
                      onRefresh={refreshLocal} 
                    />
                  </span>
                </div>
                <div className="name">{p.client_name}</div>
                <div className="row" style={{ gap: 6 }}>
                  <span>{p.client_type}</span> · <span>{p.vertical}</span>
                </div>
                <div className="desc">{p.description || "—"}</div>
                <div className="footer">
                  <span className="files">{p.engagement}</span>
                  <span>·</span>
                  <span>{p.months} mo</span>
                  <span style={{ marginLeft: "auto" }}>
                    <Icon name="clock" size={11} /> {relativeTime(p.created_at)}
                  </span>
                </div>
              </button>
            );
          })}
          {filtered.length === 0 && !loading && (
            <div className="empty" style={{ gridColumn: "1/-1" }}>
              <div className="icon"><Icon name="folder" size={20} /></div>
              <h3>No projects yet</h3>
              <p>Create your first project to get started.</p>
              <button className="btn primary" onClick={onCreate}><Icon name="plus" size={13} /> Create project</button>
            </div>
          )}
        </div>
      )}
    </div>
  );
};
const InviteModal = ({ projectId, open, onClose, onInvited }) => {
  const [query, setQuery] = React.useState("");
  const [results, setResults] = React.useState([]);
  const [searching, setSearching] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState(null);

  React.useEffect(() => {
    if (open) { setQuery(""); setResults([]); setError(null); }
  }, [open]);

  React.useEffect(() => {
    if (query.length < 2) { setResults([]); return; }
    const t = setTimeout(() => {
      setSearching(true);
      PrimaAPI.directorySearch(query)
        .then(setResults)
        .catch(() => setResults([]))
        .finally(() => setSearching(false));
    }, 300);
    return () => clearTimeout(t);
  }, [query]);

  const invite = async (person) => {
    setBusy(true);
    setError(null);
    try {
      await PrimaAPI.inviteMember(projectId, person.id);
      onInvited(person.full_name);
      onClose();
    } catch (e) {
      setError(e.message || "Failed to invite.");
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal open={open} onClose={onClose} title="Invite member"
           sub="Search for a Primus colleague. Only colleagues who have logged in to Prima will appear.">
      <div className="modal-body" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        <TextInput
          value={query}
          onChange={e => { setQuery(e.target.value); setError(null); }}
          placeholder="Search by name or email…"
          autoFocus
        />
        {searching && <div style={{ fontSize: 12, color: "var(--fg-muted)" }}>Searching…</div>}
        {results.length > 0 && (
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {results.map(p => (
              <div key={p.id} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "8px 10px", border: "1px solid var(--border)", borderRadius: "var(--r-card)", background: "var(--bg)" }}>
                <div>
                  <div style={{ fontWeight: 500, fontSize: 13 }}>{p.full_name}</div>
                  <div style={{ fontSize: 11.5, color: "var(--fg-muted)" }}>{p.designation} · {p.email}</div>
                </div>
                <button className="btn primary" style={{ padding: "4px 12px", fontSize: 12 }}
                        disabled={busy} onClick={() => invite(p)}>
                  {busy ? "…" : "Invite"}
                </button>
              </div>
            ))}
          </div>
        )}
        {!searching && query.length >= 2 && results.length === 0 && (
          <div style={{ fontSize: 12, color: "var(--fg-subtle)" }}>
            No colleagues found. They may need to log in to Prima first.
          </div>
        )}
        {error && <div style={{ fontSize: 12, color: "var(--warn, #d97706)" }}>{error}</div>}
      </div>
    </Modal>
  );
};

// ─── Team & Join requests panel ───
const TeamPanel = ({ projectId, canManage, refreshKey }) => {
  const [members, setMembers] = React.useState([]);
  const [requests, setRequests] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [busy, setBusy] = React.useState(null); // id being actioned

  const load = React.useCallback(() => {
    setLoading(true);
    Promise.all([
      PrimaAPI.listMembers(projectId).catch(() => []),
      canManage ? PrimaAPI.listJoinRequests(projectId).catch(() => []) : Promise.resolve([])
    ]).then(([m, r]) => {
      setMembers(m);
      setRequests(r);
    }).finally(() => setLoading(false));
  }, [projectId, canManage]);

  React.useEffect(() => { load(); }, [load, refreshKey]);

  const resolve = async (reqId, approve) => {
    setBusy(reqId);
    try {
      if (approve) await PrimaAPI.approveJoinRequest(projectId, reqId);
      else await PrimaAPI.rejectJoinRequest(projectId, reqId);
      load();
    } catch (e) {
      alert(e.message || "Action failed.");
    } finally {
      setBusy(null);
    }
  };

  if (loading) return <div style={{ fontSize: 12, color: "var(--fg-muted)", padding: "12px 0" }}>Loading team…</div>;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
      {/* Current Members */}
      <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
        {members.map(m => (
          <div key={m.user_id} style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 4px" }}>
            <Avatar name={m.full_name} size={24} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 12, fontWeight: 500, display: "flex", alignItems: "center", gap: 4 }}>
                {m.full_name}
                {m.role === "owner" && <span style={{ fontSize: 9, color: "var(--accent-deep)", background: "var(--bg-sunk)", padding: "1px 4px", borderRadius: 4, textTransform: "uppercase" }}>Lead</span>}
              </div>
              <div style={{ color: "var(--fg-subtle)", fontSize: 10.5 }}>{m.designation}</div>
            </div>
          </div>
        ))}
      </div>

      {/* Pending Requests */}
      {requests.length > 0 && (
        <div style={{ borderTop: "1px solid var(--border-light)", paddingTop: 10, marginTop: 4 }}>
          <div className="eyebrow" style={{ fontSize: 9, marginBottom: 8, color: "var(--warn, #d97706)" }}>Pending Join Requests</div>
          {requests.map(r => (
            <div key={r.id} style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 10px", background: "var(--bg-sunk)", borderRadius: "var(--r-input, 6px)", fontSize: 12 }}>
              <Avatar name={r.requester_name} size={26} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontWeight: 500 }}>{r.requester_name}</div>
                <div style={{ color: "var(--fg-subtle)", fontSize: 11 }}>{r.requester_designation} · {relativeTime(r.created_at)}</div>
              </div>
              <div style={{ display: "flex", gap: 4 }}>
                <button className="btn sm" style={{ color: "var(--ok, #16a34a)", padding: "2px 6px" }} disabled={busy === r.id}
                        onClick={() => resolve(r.id, true)}>
                  <Icon name="check" size={10} />
                </button>
                <button className="btn sm" style={{ color: "var(--err, #dc2626)", padding: "2px 6px" }} disabled={busy === r.id}
                        onClick={() => resolve(r.id, false)}>
                  <Icon name="x" size={10} />
                </button>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

// ─── Project detail ───
const ProjectDetail = ({ project, onBack, onUpload, currentUser, tier, membership, showRbac }) => {
  const [activeFolder, setActiveFolder] = React.useState("01");
  const [hoverInfo, setHoverInfo] = React.useState(null);
  const [files, setFiles] = React.useState([]);
  const [loadingFiles, setLoadingFiles] = React.useState(false);
  const [refreshKey, setRefreshKey] = React.useState(0);
  const [showInvite, setShowInvite] = React.useState(false);
  const isLeadership = tier === "leadership";
  const isOwner = membership === "owner";
  const isMember = membership === "owner" || membership === "member";
  const restrictedFolder = activeFolder === "06" && !isLeadership && !isOwner;
  const canUpload = isLeadership || isMember;

  const refreshFiles = React.useCallback(() => setRefreshKey(k => k + 1), []);

  // Load artifacts whenever folder changes (skip restricted folder unless authorized)
  React.useEffect(() => {
    if (restrictedFolder) { setFiles([]); return; }
    setLoadingFiles(true);
    PrimaAPI.listArtifacts(project.id, activeFolder)
      .then(data => setFiles((data || []).map(toDisplayFile)))
      .catch(() => setFiles([]))
      .finally(() => setLoadingFiles(false));
  }, [project.id, activeFolder, restrictedFolder, refreshKey]);

  return (
    <div className="page">
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 16 }}>
        <button className="btn ghost" onClick={onBack}><Icon name="chevron-left" size={14} /> Workspace</button>
        <span className="tag">{project.code}</span>
        <ProjectStatusTag 
          project={project} 
          canEdit={isLeadership || isOwner} 
          onRefresh={onBack} 
        />
      </div>
      <div className="page-head">
        <div>
          <div className="eyebrow">{project.client_type} · {project.code}</div>
          <h1 className="page-title">{project.client_name}</h1>
          <p className="page-sub">{project.description || "—"}</p>
          {project.client_details && (
            <p style={{ fontSize: 12.5, color: "var(--fg-subtle)", marginTop: 4, lineHeight: 1.5 }}>
              <Icon name="info" size={11} style={{ marginRight: 5, verticalAlign: "middle" }} />
              {project.client_details}
            </p>
          )}
          <div style={{ display: "flex", gap: 6, marginTop: 12, flexWrap: "wrap" }}>
            <span className="tag accent">{project.vertical}</span>
            <span className="tag">{project.engagement}</span>
            <span className="tag">{project.months} mo</span>
          </div>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn primary" onClick={() => onUpload(activeFolder)} disabled={!canUpload}>
            <Icon name="upload" size={14} /> Upload artifact
          </button>
        </div>
      </div>

      <div className="workspace-split">
        <div>
          <div className="section-title" style={{ marginTop: 0 }}>Folder hierarchy</div>
          <div className="folder-tree">
            {FOLDERS.map(f => (
              <div key={f.id}
                   className="folder-row"
                   data-active={activeFolder === f.id}
                   onClick={() => setActiveFolder(f.id)}>
                <span className="num">{f.id}</span>
                <div style={{ display: "flex", alignItems: "center", gap: 8, minWidth: 0 }}>
                  <Icon name="folder" size={15} />
                  <span style={{ fontFamily: "var(--font-mono)", fontSize: 12.5, fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
                    {f.name}
                  </span>
                </div>
                <span className="file-count">{activeFolder === f.id ? files.length : "—"}</span>
                <span className="info-btn"
                      onMouseEnter={() => setHoverInfo(f.id)}
                      onMouseLeave={() => setHoverInfo(null)}
                      onClick={(e) => e.stopPropagation()}>
                  i
                  {hoverInfo === f.id && <span className="tooltip">{f.tip}</span>}
                </span>
              </div>
            ))}
          </div>
          <div className="card" style={{ marginTop: 16 }}>
            <div className="card-body" style={{ padding: 14 }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
                <div className="eyebrow"><Icon name="shield" size={11} /> Governance & Team</div>
                {(isOwner || isLeadership) && (
                  <button className="btn ghost" style={{ fontSize: 10.5, padding: "2px 8px" }}
                          onClick={() => setShowInvite(true)}>
                    <Icon name="plus" size={11} /> Invite
                  </button>
                )}
              </div>
              
              <div style={{ fontSize: 12, color: "var(--fg-muted)", lineHeight: 1.5, marginBottom: 12 }}>
                {isLeadership
                  ? <><strong>Tier 1 Leadership</strong> · Full project authority.</>
                  : isOwner
                  ? <><strong>Project Owner</strong> · Manage team & uploads.</>
                  : isMember
                  ? <><strong>Member</strong> · Collaboration access.</>
                  : <><strong>Guest</strong> · View-only.</>}
              </div>

              <div style={{ marginBottom: 16, display: "flex", flexWrap: "wrap", gap: 4 }}>
                <span className="rbac-badge" style={{ fontSize: 10 }}>View</span>
                {(isLeadership || isMember) && <span className="rbac-badge" style={{ fontSize: 10 }}>Upload</span>}
                {(isLeadership || isOwner) && <span className="rbac-badge" style={{ fontSize: 10 }}>Manage Team</span>}
              </div>

              <div style={{ borderTop: "1px solid var(--border-light)", paddingTop: 12 }}>
                <TeamPanel projectId={project.id} canManage={isOwner || isLeadership} refreshKey={refreshKey} />
              </div>
            </div>
          </div>
          </div>
          <InviteModal
            projectId={project.id}
            open={showInvite}
            onClose={() => setShowInvite(false)}
            onInvited={(name) => { setShowInvite(false); refreshFiles(); }}
          /> {/* Artifacts panel */}
          <div>
            <div className="section-title" style={{ marginTop: 0, justifyContent: "space-between" }}>
              <span>{FOLDERS.find(f => f.id === activeFolder)?.name}</span>
              <span style={{ fontSize: 11, color: "var(--fg-subtle)" }}>{files.length} files</span>
            </div>

          {restrictedFolder ? (
            <div className="empty">
              <div className="icon"><Icon name="lock" size={20} /></div>
              <h3>Restricted · Leadership or Owner</h3>
              <p>Folder 06 holds invoices, POs, and budget trackers. Only the project owner and Tier 1 (VP+) can view.</p>
            </div>
          ) : loadingFiles ? (
            <div className="empty">
              <div style={{ color: "var(--fg-muted)", fontFamily: "var(--font-mono)", fontSize: 13 }}>Loading…</div>
            </div>
          ) : files.length === 0 ? (
            <div className="empty">
              <div className="icon"><Icon name="upload" size={18} /></div>
              <h3>This folder is empty</h3>
              <p>Drop files here or click upload to get started. Each upload prompts for metadata.</p>
              {canUpload && <button className="btn primary" onClick={() => onUpload(activeFolder)}><Icon name="upload" size={13} /> Upload artifact</button>}
            </div>
          ) : (
            <div className="folder-tree" style={{ height: 420, overflowY: "auto", background: "var(--bg-elev)", overflowX: "visible" }}>
              <div className="file-list" style={{ border: "none", overflow: "visible" }}>
                <div className="file-list-head" style={{ position: "sticky", top: 0, background: "var(--bg-elev)", zIndex: 1, borderTop: "none" }}>
                  <span></span>
                  <span>Name</span>
                  <span>Type</span>
                  <span>Owner</span>
                  <span>Confidentiality</span>
                  <span style={{ textAlign: "right" }}>Added</span>
                  <span></span>
                </div>
                {files.map((f, i) => (
                  <div key={f.id || i} className="file-row" style={{ borderBottom: "1px solid var(--hairline)", overflow: "visible" }}>
                    <div className={"file-icon " + f.ext}>{f.ext.toUpperCase()}</div>
                    <div className="file-name">
                      {f.name}
                      <span className="sub">{f.governance.length ? f.governance.join(" · ") : ""}</span>
                    </div>
                    <span style={{ fontSize: 12.5, color: "var(--fg-muted)" }}>{f.type}</span>
                    <span style={{ fontSize: 12.5 }}>{f.owner}</span>
                    <ConfDot level={f.conf} />
                    <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--fg-subtle)", textAlign: "right" }}>
                      {f.added}
                    </span>
                    <FileActionMenu file={f} currentUser={currentUser} tier={tier} onRefresh={refreshFiles} />
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

Object.assign(window, { Sidebar, Topbar, ProjectWorkspace, ProjectDetail });
