/**
 * Dashboard top-nav chip listing active enterprise marketplace sources.
 *
 *   - No keys configured → renders a single dim '+ Enterprise' button
 *     that opens the Add-tenant form. Most non-enterprise users won't
 *     even notice it.
 *   - 1+ keys           → '🔒 Fortinet' (single) or '🔒 Fortinet +1'
 *     (collapsed badge). Click opens a popover with the full list,
 *     per-tenant Re-sync / Reinstall / Remove buttons, sync status,
 *     and an inline 'Add another tenant' input.
 *
 * Replaces the old behaviour of jumping into Settings — most enterprise
 * lifecycle actions (resync, reinstall, add, remove, see status) now
 * live in this single popover, which is where users instinctively look
 * once they've spotted the 🔒 chip.
 */

import { useEffect, useRef, useState } from 'react';

interface SourceView {
  tenant_id: string;
  display_name: string;
  repo_url: string;
  priority: number;
  pat_preview: string;
  last_sync?: { ok: boolean; error?: string; fetched_at: string } | null;
  departments?: Array<{ id: string; display_name: string; has_template?: boolean }>;
  active_dept?: string | null;
}

export default function EnterpriseBadge({ onOpenSettings: _onOpenSettings }: { onOpenSettings: () => void }) {
  const [sources, setSources] = useState<SourceView[]>([]);
  const [open, setOpen] = useState(false);
  const [busy, setBusy] = useState<'resync' | 'reinstall' | string | null>(null);
  const [status, setStatus] = useState('');
  const [error, setError] = useState('');
  const [showAddInline, setShowAddInline] = useState(false);
  const [newKey, setNewKey] = useState('');
  // Edit-key flow: `editTenant` is the tenant_id currently being edited
  // (null when not editing). `editKey` is the new PAT/long-form key the
  // user is typing. Kept separate from add-flow state so the row's
  // pencil button doesn't collide with the bottom + Add tenant input.
  const [editTenant, setEditTenant] = useState<string | null>(null);
  const [editKey, setEditKey] = useState('');
  // Phase 2: web SSO sweep. Reuses /api/login-status — same probe path
  // as the LoginStatusPanel, just inlined here so users have ONE place
  // for unified-auth actions.
  const [webRows, setWebRows] = useState<Array<{
    id: string;
    label: string;
    refresh: { kind: string; url?: string };
    ok: boolean | null;            // null = never checked
    message: string;
    checked_at: number | null;
  }>>([]);
  const [webBusy, setWebBusy] = useState(false);
  const [webErr, setWebErr] = useState('');
  // Phase 3: Unified IdP login — one user/pass/OTP entry → extension
  // drives the SAML IdP form, all SPs piggy-back the resulting cookie.
  const [idpUser, setIdpUser] = useState('');
  // Prefill: saved credentials win; else fall back to email local-part.
  // The saved-creds endpoint returns the plaintext password (AES-decrypted
  // on read) so the user only needs to type OTP.
  useEffect(() => {
    if (idpUser || idpPass) return;
    (async () => {
      try {
        const r = await fetch('/api/auth/idp-credentials');
        if (r.ok) {
          const c = await r.json();
          if (c?.username && c?.password) {
            setIdpUser(c.username);
            setIdpPass(c.password);
            setIdpSaveCreds(true);
            setIdpHasSaved(true);
            return;
          }
        }
      } catch { /* fall through */ }
      try {
        const sr = await fetch('/api/settings');
        if (sr.ok) {
          const s = await sr.json();
          const email = (s?.displayEmail || '').trim();
          if (email) {
            const local = email.includes('@') ? email.split('@')[0] : email;
            if (local) setIdpUser(local);
          }
        }
      } catch { /* ignore */ }
    })();
  }, []);
  const [idpPass, setIdpPass] = useState('');
  const [idpPassVisible, setIdpPassVisible] = useState(false);
  // "Save credentials" checkbox state. When true on successful login,
  // username + password are POSTed to /api/auth/idp-credentials (the
  // backend AES-encrypts the password). On mount, we load any saved
  // creds and prefill so the user only types OTP.
  const [idpSaveCreds, setIdpSaveCreds] = useState(false);
  const [idpHasSaved, setIdpHasSaved] = useState(false);
  const [idpOtp, setIdpOtp] = useState('');
  const [idpBusy, setIdpBusy] = useState(false);
  const [idpErr, setIdpErr] = useState('');
  const [idpStatus, setIdpStatus] = useState('');
  // Terminal commands that failed to auto-run — surfaced so the user
  // can paste them manually (Accessibility denied, no Terminal open,
  // etc.). Cleared on every new login attempt.
  const [idpManualCommands, setIdpManualCommands] = useState<Array<{ name: string; command: string; error?: string }>>([]);
  // IdP entries that failed (different credentials required, etc.) —
  // surface the trigger URL so the user can complete login by hand.
  const [idpManualUrls, setIdpManualUrls] = useState<Array<{ host: string; url: string; error?: string }>>([]);
  const popoverRef = useRef<HTMLDivElement>(null);

  const load = async () => {
    try {
      const r = await fetch('/api/enterprise-keys');
      if (!r.ok) return;
      const data = await r.json();
      setSources((data?.sources || []) as SourceView[]);
    } catch {
      // Network blip — leave previous state.
    }
  };

  type LoginRow = {
    source: { id: string; label: string; category: string; refresh: { kind: string; url?: string } };
    result: { ok: boolean; message?: string; checked_at: number } | null;
  };
  const ingestLoginRows = (rows: LoginRow[]) => {
    setWebRows(
      rows
        .filter(r => r.source.category === 'browser')
        .map(r => ({
          id: r.source.id,
          label: r.source.label,
          refresh: r.source.refresh,
          ok: r.result ? r.result.ok : null,
          message: r.result?.message || '',
          checked_at: r.result?.checked_at || null,
        })),
    );
  };

  const loadWebSessions = async () => {
    try {
      const r = await fetch('/api/web-sessions');
      if (!r.ok) return;
      const data = await r.json();
      ingestLoginRows((data?.rows || []) as LoginRow[]);
    } catch { /* leave previous */ }
  };

  const handleCheckAllWeb = async () => {
    if (webBusy) return;
    setWebBusy(true); setWebErr('');
    try {
      const r = await fetch('/api/web-sessions', { method: 'POST' });
      const data = await r.json();
      if (!r.ok || data?.error) {
        setWebErr(data?.error || `HTTP ${r.status}`);
      } else {
        const rows = (data?.rows || []) as LoginRow[];
        ingestLoginRows(rows);
        // Auto-open the failed browser sources — that's the whole point
        // of this section. Pops up a tab per failed site so the user can
        // complete IdP login. Done sequentially with a tiny delay so the
        // popup blocker treats the first one as user-initiated.
        const failed = rows.filter(r =>
          r.source.category === 'browser' &&
          r.result && !r.result.ok &&
          r.source.refresh.kind === 'open-url' &&
          r.source.refresh.url,
        );
        failed.forEach((r, i) => setTimeout(() => {
          window.open(r.source.refresh.url!, '_blank', 'noopener');
        }, i * 250));
      }
    } catch (e) {
      setWebErr(e instanceof Error ? e.message : String(e));
    } finally {
      setWebBusy(false);
    }
  };

  const handleOpenWeb = (row: { refresh: { kind: string; url?: string } }) => {
    if (row.refresh.kind === 'open-url' && row.refresh.url) {
      window.open(row.refresh.url, '_blank', 'noopener');
    }
  };

  const handleIdpLogin = async () => {
    if (idpBusy) return;
    if (!idpUser.trim() || !idpPass || !idpOtp.trim()) {
      setIdpErr('Username, password, and OTP are all required');
      return;
    }
    setIdpBusy(true); setIdpErr(''); setIdpStatus(''); setIdpManualCommands([]); setIdpManualUrls([]);
    try {
      const r = await fetch('/api/auth/idp-login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username: idpUser.trim(), password: idpPass, otp: idpOtp.trim() }),
      });
      const data = await r.json();
      // Template may declare multiple IdPs (e.g. FAC for mantis/pmdb/tp +
      // a regional SAML for scap). data.idp_logins holds per-IdP results.
      // Render per-entry status so partial failures are visible.
      const logins = Array.isArray(data.idp_logins) ? data.idp_logins : [];
      const parts: string[] = [];
      const failedTerminals: Array<{ name: string; command: string; error?: string }> = [];
      const failedIdpUrls: Array<{ host: string; url: string; error?: string }> = [];
      for (const entry of logins as Array<{
        idp_host: string;
        trigger_url?: string;
        ok: boolean;
        steps_completed?: number;
        error?: string;
        post_login_terminal?: Array<{ name: string; ok: boolean; command?: string; error?: string }>;
      }>) {
        if (entry.ok) {
          parts.push(`✓ ${entry.idp_host} (${entry.steps_completed || 0} steps)`);
        } else {
          parts.push(`✗ ${entry.idp_host}: ${entry.error || 'failed'}`);
          if (entry.trigger_url) {
            failedIdpUrls.push({ host: entry.idp_host, url: entry.trigger_url, error: entry.error });
          }
        }
        if (Array.isArray(entry.post_login_terminal)) {
          for (const r of entry.post_login_terminal) {
            parts.push(r.ok ? `${r.name} ✓` : `${r.name}: ${r.error || 'skipped'}`);
            if (!r.ok && r.command) {
              failedTerminals.push({ name: r.name, command: r.command, error: r.error });
            }
          }
        }
      }
      setIdpManualCommands(failedTerminals);
      setIdpManualUrls(failedIdpUrls);
      if (!data.ok) {
        // Surface aggregate error but keep per-entry breakdown if we have one.
        setIdpErr(parts.length > 0 ? parts.join(' · ') : (data.error || 'IdP login failed'));
        return;
      }
      // Persist creds AFTER a successful login so we never store a
      // password the IdP just rejected. Encrypted at rest server-side.
      if (idpSaveCreds && idpUser.trim() && idpPass) {
        try {
          await fetch('/api/auth/idp-credentials', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ username: idpUser.trim(), password: idpPass }),
          });
          setIdpHasSaved(true);
        } catch { /* non-fatal */ }
      }
      // Only wipe OTP after success — keep password for follow-up Login
      // All Sites within the same session (OTP is single-use anyway).
      setIdpOtp('');
      if (!idpSaveCreds) setIdpPass('');
      setIdpStatus(`${parts.join(' · ')}. Re-probing sessions…`);
      await handleCheckAllWeb();
    } catch (e) {
      setIdpErr(e instanceof Error ? e.message : String(e));
    } finally {
      setIdpBusy(false);
    }
  };

  useEffect(() => {
    load();
    loadWebSessions();
    const onFocus = () => { load(); loadWebSessions(); };
    window.addEventListener('focus', onFocus);
    return () => window.removeEventListener('focus', onFocus);
  }, []);

  // Refresh web rows when popover opens (so the dropdown is fresh).
  useEffect(() => { if (open) { loadWebSessions(); } }, [open]);

  // Close popover on outside click.
  useEffect(() => {
    if (!open) return;
    const onClick = (e: MouseEvent) => {
      if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) {
        setOpen(false);
      }
    };
    window.addEventListener('mousedown', onClick);
    return () => window.removeEventListener('mousedown', onClick);
  }, [open]);

  const reset = () => { setStatus(''); setError(''); };

  // Single Re-sync: pulls registry.json AND re-fetches installed
  // connector manifests when their version changes. The old
  // "Reinstall all" button (force-overwrite regardless of version)
  // was removed — too many footguns; rare cases can re-run the wizard
  // or use the per-row ↻ in the connectors panel.
  const handleResync = async () => {
    reset();
    setBusy('resync');
    try {
      const r = await fetch('/api/enterprise-keys', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'resync', refreshInstalled: true }),
      });
      const data = await r.json();
      if (!data.ok) { setError(data.error || 'resync failed'); }
      else {
        const result = data.result || {};
        const okN = (result.sources || []).filter((s: any) => s.ok).length;
        const total = (result.sources || []).length;
        const refreshed = result.manifests_refreshed || 0;
        setStatus(`✓ Re-synced ${okN}/${total} sources${refreshed ? ` · refreshed ${refreshed} manifests` : ''}`);
      }
      await load();
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setBusy(null);
    }
  };

  const handleEdit = async (tenant_id: string, display_name: string) => {
    const key = editKey.trim();
    if (!key) return;
    reset();
    setBusy(`edit:${tenant_id}`);
    try {
      const r = await fetch(`/api/enterprise-keys?tenant_id=${encodeURIComponent(tenant_id)}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ key }),
      });
      const data = await r.json();
      if (!data.ok) setError(data.error || 'edit failed');
      else {
        setStatus(`✓ Updated ${display_name} — key rotated`);
        setEditTenant(null);
        setEditKey('');
      }
      await load();
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setBusy(null);
    }
  };

  const handleRemove = async (tenant_id: string, display_name: string) => {
    if (!confirm(`Remove enterprise source "${display_name}"?\n\nInstalled connectors stay on disk; they just stop receiving updates.`)) return;
    reset();
    setBusy(`remove:${tenant_id}`);
    try {
      const r = await fetch(`/api/enterprise-keys?tenant_id=${encodeURIComponent(tenant_id)}`, { method: 'DELETE' });
      const data = await r.json();
      if (!data.ok) setError(data.error || 'remove failed');
      else setStatus(`✓ Removed ${display_name}`);
      await load();
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setBusy(null);
    }
  };

  const handleAdd = async () => {
    const key = newKey.trim();
    if (!key) return;
    reset();
    setBusy('add');
    try {
      const r = await fetch('/api/enterprise-keys', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ key }),
      });
      const data = await r.json();
      if (!data.ok) setError(data.error || 'failed to add key');
      else {
        setNewKey('');
        setShowAddInline(false);
        // E4: when the new tenant landed a wizard template on disk, hand
        // off to the onboarding wizard scoped to this source so the user
        // can apply its defaults right away. Dispatched via the existing
        // forge:open-onboarding event so SettingsModal isn't a required
        // intermediate. Falls back to the status pill when no template
        // is available (public-only tenant).
        if (data.has_wizard_template && data.source_id) {
          setStatus(`✓ Added ${data.tenant_id} — opening wizard…`);
          window.dispatchEvent(new CustomEvent('forge:open-onboarding', {
            detail: { source_id: data.source_id },
          }));
          setOpen(false);
        } else {
          setStatus(`✓ Added ${data.tenant_id} — marketplace synced`);
        }
      }
      await load();
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setBusy(null);
    }
  };

  // When there are no keys, render a compact "+ Enterprise" chip so the
  // entry point still exists for power users. Non-enterprise people can
  // ignore it.
  if (sources.length === 0) {
    return (
      <div className="relative" ref={popoverRef}>
        <button
          onClick={() => { setOpen(v => !v); setShowAddInline(true); }}
          title="Add a private enterprise marketplace key"
          className="text-[10px] px-2 py-0.5 rounded border border-dashed border-[var(--text-secondary)]/30 text-[var(--text-secondary)] hover:border-[var(--text-primary)]/40 hover:text-[var(--text-primary)] transition-colors"
        >
          + Enterprise
        </button>
        {open && <AddTenantPopover newKey={newKey} setNewKey={setNewKey} onSubmit={handleAdd} onCancel={() => setOpen(false)} busy={busy === 'add'} error={error} status={status} />}
      </div>
    );
  }

  const first = sources[0];
  const extra = sources.length - 1;

  return (
    <div className="relative" ref={popoverRef}>
      <button
        onClick={() => setOpen(v => !v)}
        title="Click for enterprise actions (re-sync, reinstall, add tenant)"
        className="text-[10px] px-2 py-0.5 rounded bg-amber-500/15 text-amber-400 hover:bg-amber-500/25 transition-colors flex items-center gap-1"
      >
        <span>🔒</span>
        <span className="font-medium">{first.display_name}</span>
        {extra > 0 && <span className="opacity-70">+{extra}</span>}
        <span className="opacity-60 text-[9px]">▾</span>
      </button>
      {open && (
        <div className="absolute left-0 top-full mt-1 w-[360px] z-50 bg-[var(--bg-secondary)] border border-[var(--border)] rounded shadow-lg p-2 space-y-2">
          {/* Phase 3 — Unified IdP login. One user/pass/OTP entry →
              extension drives the SAML IdP form → every SAML SP gets
              its cookie via the resulting session. Only shown when at
              least one web connector exists (no IdP, no point). */}
          {webRows.length > 0 && (
            <div className="border border-[var(--border)] rounded px-2 py-1.5 space-y-1.5">
              <div className="flex items-center gap-1.5">
                <span className="text-[10px] uppercase tracking-wider text-[var(--text-secondary)]">
                  Unified login
                </span>
                <span className="text-[9px] text-[var(--text-secondary)] ml-auto">
                  one fill → all SAML sites
                </span>
              </div>
              <div className="grid grid-cols-2 gap-1">
                <input
                  type="text"
                  placeholder="Username"
                  autoComplete="username"
                  value={idpUser}
                  onChange={(e) => setIdpUser(e.target.value)}
                  className="text-xs px-2 py-1 bg-[var(--bg)] border border-[var(--border)] rounded"
                  disabled={idpBusy}
                />
                <div className="relative">
                  <input
                    type={idpPassVisible ? 'text' : 'password'}
                    placeholder="Password"
                    autoComplete="current-password"
                    value={idpPass}
                    onChange={(e) => setIdpPass(e.target.value)}
                    className="text-xs px-2 py-1 pr-7 bg-[var(--bg)] border border-[var(--border)] rounded w-full"
                    disabled={idpBusy}
                  />
                  <button
                    type="button"
                    onClick={() => setIdpPassVisible((v) => !v)}
                    tabIndex={-1}
                    title={idpPassVisible ? 'Hide password' : 'Show password'}
                    className="absolute right-1 top-1/2 -translate-y-1/2 px-1 text-[var(--text-secondary)] hover:text-[var(--text)] text-[11px] leading-none"
                  >
                    {idpPassVisible ? '🙈' : '👁'}
                  </button>
                </div>
              </div>
              <div className="flex gap-1">
                <input
                  type="text"
                  inputMode="numeric"
                  pattern="\d*"
                  autoComplete="one-time-code"
                  maxLength={8}
                  placeholder="6-digit OTP"
                  value={idpOtp}
                  onChange={(e) => setIdpOtp(e.target.value.replace(/\D/g, ''))}
                  onKeyDown={(e) => { if (e.key === 'Enter') handleIdpLogin(); }}
                  className="flex-1 text-xs px-2 py-1 bg-[var(--bg)] border border-[var(--border)] rounded font-mono tracking-wider"
                  disabled={idpBusy}
                />
                <button
                  onClick={handleIdpLogin}
                  disabled={idpBusy || !idpUser.trim() || !idpPass || !idpOtp.trim()}
                  className="text-[10px] px-2 py-1 rounded bg-amber-500/20 text-amber-400 hover:bg-amber-500/30 disabled:opacity-40 disabled:cursor-not-allowed"
                >
                  {idpBusy ? '…' : 'Login all sites'}
                </button>
              </div>
              <div className="flex items-center gap-2 text-[10px] text-[var(--text-secondary)]">
                <label className="flex items-center gap-1 cursor-pointer select-none">
                  <input
                    type="checkbox"
                    checked={idpSaveCreds}
                    onChange={(e) => setIdpSaveCreds(e.target.checked)}
                    className="accent-amber-500"
                  />
                  <span>Save user + password (AES-encrypted)</span>
                </label>
                {idpHasSaved && (
                  <button
                    type="button"
                    onClick={async () => {
                      try { await fetch('/api/auth/idp-credentials', { method: 'DELETE' }); } catch {}
                      setIdpUser(''); setIdpPass(''); setIdpHasSaved(false); setIdpSaveCreds(false);
                    }}
                    className="ml-auto text-red-400 hover:text-red-300 underline decoration-dotted"
                    title="Forget saved username + password"
                  >
                    Clear saved
                  </button>
                )}
              </div>
              {idpErr && <div className="text-[10px] text-red-400">{idpErr}</div>}
              {idpStatus && <div className="text-[10px] text-emerald-400">{idpStatus}</div>}
              {idpManualUrls.length > 0 && (
                <div className="text-[10px] text-amber-400 space-y-1 mt-1">
                  <div>Some IdPs require different credentials — open and log in manually:</div>
                  {idpManualUrls.map((u, i) => (
                    <div key={i} className="flex items-center gap-1">
                      <a
                        href={u.url}
                        target="_blank"
                        rel="noopener"
                        title={u.error || u.host}
                        className="flex-1 bg-black/30 px-1.5 py-0.5 rounded font-mono text-[10px] break-all hover:bg-black/40 underline"
                      >
                        {u.url}
                      </a>
                    </div>
                  ))}
                </div>
              )}
              {idpManualCommands.length > 0 && (
                <div className="text-[10px] text-amber-400 space-y-1 mt-1">
                  <div>Auto-run failed — paste these in your Terminal:</div>
                  {idpManualCommands.map((c, i) => (
                    <div key={i} className="flex items-center gap-1">
                      <code
                        className="flex-1 bg-black/30 px-1.5 py-0.5 rounded font-mono text-[10px] break-all cursor-pointer hover:bg-black/40"
                        title={c.error ? `Reason: ${c.error}` : 'Click to copy'}
                        onClick={() => { navigator.clipboard.writeText(c.command).catch(() => {}); }}
                      >
                        {c.command}
                      </code>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}

          {/* Phase 2 — Web SSO sweep. Same probe path as LoginStatusPanel,
              inlined here so users have ONE dropdown for unified auth. */}
          {webRows.length > 0 && (
            <div className="border border-[var(--border)] rounded px-2 py-1.5 space-y-1.5">
              <div className="flex items-center gap-1.5">
                <span className="text-[10px] uppercase tracking-wider text-[var(--text-secondary)]">
                  Web sessions ({webRows.length})
                </span>
                <button
                  onClick={handleCheckAllWeb}
                  disabled={webBusy}
                  className="ml-auto text-[10px] px-2 py-0.5 rounded bg-amber-500/20 text-amber-400 hover:bg-amber-500/30 disabled:opacity-40"
                  title="Probe each site via the extension. Opens login pages for any that fail."
                >
                  {webBusy ? '…' : 'Check & open failed'}
                </button>
              </div>
              <div className="space-y-1">
                {webRows.map(row => {
                  const dot = row.ok === null ? '⚪' : row.ok ? '🟢' : '🔴';
                  const canOpen = row.refresh.kind === 'open-url' && !!row.refresh.url;
                  return (
                    <div key={row.id} className="flex items-center gap-1.5 text-[10px]">
                      <span>{dot}</span>
                      <span className="text-[var(--text-primary)] truncate flex-1" title={row.message}>
                        {row.label}
                      </span>
                      {canOpen && (
                        <button
                          onClick={() => handleOpenWeb(row)}
                          className="text-[9px] px-1.5 py-0.5 rounded border border-[var(--border)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
                          title={`Open ${row.refresh.url}`}
                        >
                          Open
                        </button>
                      )}
                    </div>
                  );
                })}
              </div>
              {webErr && <div className="text-[10px] text-red-400">{webErr}</div>}
            </div>
          )}

          <div className="text-[10px] text-[var(--text-secondary)] uppercase tracking-wider px-1">
            Enterprise tenants ({sources.length})
          </div>

          {/* Per-tenant rows */}
          <div className="space-y-1">
            {sources.map(s => {
              const sync = s.last_sync;
              const ok = sync?.ok;
              const failed = sync && !sync.ok;
              return (
                <div key={s.tenant_id} className="border border-[var(--border)] rounded px-2 py-1.5 space-y-1">
                  <div className="flex items-center gap-1.5">
                    <span className="text-[10px] px-1.5 py-0.5 rounded bg-amber-500/15 text-amber-400 shrink-0">
                      🔒 {s.display_name}
                    </span>
                    <span className="text-[9px] text-[var(--text-secondary)] ml-auto">
                      priority {s.priority}
                    </span>
                  </div>
                  <div className="text-[9px] font-mono text-[var(--text-secondary)] truncate" title={s.repo_url}>
                    {s.repo_url}
                  </div>
                  <div className="text-[9px] text-[var(--text-secondary)] flex items-center gap-2">
                    <span>PAT {s.pat_preview}</span>
                    {ok && <span className="text-emerald-500">● synced</span>}
                    {failed && <span className="text-red-400">✗ failed</span>}
                    {!sync && <span className="italic">(not yet)</span>}
                  </div>
                  {/* Dept lives in Settings → Identity (profile field).
                      No longer shown on the tenant row. */}
                  {failed && sync?.error && (
                    <div className="text-[9px] text-red-300 border border-red-500/30 bg-red-500/5 rounded px-1.5 py-1 leading-snug whitespace-pre-wrap break-words">
                      {sync.error}
                    </div>
                  )}
                  {editTenant === s.tenant_id ? (
                    <div className="space-y-1 pt-1 border-t border-[var(--border)]">
                      <input
                        type="password"
                        value={editKey}
                        onChange={(e) => setEditKey(e.target.value)}
                        placeholder="new PAT or full <pat>@<repo>"
                        className="w-full text-[10px] px-1.5 py-1 rounded border border-[var(--border)] bg-[var(--bg-tertiary)] text-[var(--text-primary)]"
                        autoFocus
                      />
                      <div className="flex gap-1 justify-end">
                        <button
                          onClick={() => { setEditTenant(null); setEditKey(''); }}
                          className="text-[9px] px-1.5 py-0.5 rounded text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
                        >
                          Cancel
                        </button>
                        <button
                          onClick={() => handleEdit(s.tenant_id, s.display_name)}
                          disabled={!editKey.trim() || busy === `edit:${s.tenant_id}`}
                          className="text-[9px] px-1.5 py-0.5 rounded border border-[var(--accent)]/40 text-[var(--accent)] hover:bg-[var(--accent)]/10 disabled:opacity-40"
                        >
                          {busy === `edit:${s.tenant_id}` ? 'Saving…' : 'Save'}
                        </button>
                      </div>
                    </div>
                  ) : (
                    <div className="flex justify-end gap-1">
                      <button
                        onClick={() => { setEditTenant(s.tenant_id); setEditKey(''); reset(); }}
                        className="text-[9px] px-1.5 py-0.5 rounded border border-[var(--border)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
                        title="Rotate the PAT for this tenant in place"
                      >
                        ✎ Edit
                      </button>
                      <button
                        onClick={() => handleRemove(s.tenant_id, s.display_name)}
                        disabled={busy === `remove:${s.tenant_id}`}
                        className="text-[9px] px-1.5 py-0.5 rounded border border-red-500/30 text-red-400 hover:bg-red-500/10 disabled:opacity-40"
                      >
                        Remove
                      </button>
                    </div>
                  )}
                </div>
              );
            })}
          </div>

          {/* Actions (apply to ALL tenants) */}
          <div className="flex gap-1.5 pt-1 border-t border-[var(--border)]">
            <button
              onClick={() => handleResync()}
              disabled={!!busy}
              className="text-[10px] px-2 py-1 rounded border border-[var(--border)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] disabled:opacity-40"
              title="Re-fetch registry.json from every source AND re-pull installed connector manifests when their version changed."
            >
              {busy === 'resync' ? 'Syncing…' : '↻ Re-sync'}
            </button>
            {!showAddInline && (
              <button
                onClick={() => setShowAddInline(true)}
                className="text-[10px] px-2 py-1 rounded text-[var(--text-secondary)] hover:text-[var(--text-primary)] ml-auto"
              >
                + Add tenant
              </button>
            )}
          </div>

          {/* Inline add */}
          {showAddInline && (
            <div className="border-t border-[var(--border)] pt-2 space-y-1.5">
              <label className="text-[9px] text-[var(--text-secondary)] block">
                Add another enterprise tenant
              </label>
              <input
                type="text"
                placeholder="acme:github_pat_xxx"
                value={newKey}
                onChange={(e) => setNewKey(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter' && !busy) handleAdd(); }}
                className="w-full text-[10px] font-mono px-2 py-1 rounded bg-[var(--bg-primary)] border border-[var(--border)] text-[var(--text-primary)] placeholder:text-[var(--text-secondary)]/50"
              />
              <div className="flex gap-1.5">
                <button
                  onClick={handleAdd}
                  disabled={busy === 'add' || !newKey.trim()}
                  className="text-[10px] px-2 py-1 rounded bg-[var(--accent)] text-white hover:opacity-90 disabled:opacity-40"
                >
                  {busy === 'add' ? 'Adding…' : 'Add'}
                </button>
                <button
                  onClick={() => { setShowAddInline(false); setNewKey(''); }}
                  className="text-[10px] px-2 py-1 text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
                >
                  Cancel
                </button>
              </div>
            </div>
          )}

          {/* Status / error */}
          {error && (
            <div className="text-[9px] text-red-400 border border-red-500/30 bg-red-500/5 rounded px-1.5 py-1">
              {error}
            </div>
          )}
          {status && !error && (
            <div className="text-[9px] text-emerald-400">{status}</div>
          )}
        </div>
      )}
    </div>
  );
}

function AddTenantPopover({
  newKey, setNewKey, onSubmit, onCancel, busy, error, status,
}: {
  newKey: string;
  setNewKey: (s: string) => void;
  onSubmit: () => void;
  onCancel: () => void;
  busy: boolean;
  error: string;
  status: string;
}) {
  return (
    <div className="absolute left-0 top-full mt-1 w-[320px] z-50 bg-[var(--bg-secondary)] border border-[var(--border)] rounded shadow-lg p-2 space-y-1.5">
      <div className="text-[10px] text-[var(--text-secondary)]">
        Add a private enterprise marketplace key. Forge will sync immediately after.
      </div>
      <input
        type="text"
        placeholder="fortinet:github_pat_xxx"
        value={newKey}
        onChange={(e) => setNewKey(e.target.value)}
        onKeyDown={(e) => { if (e.key === 'Enter' && !busy) onSubmit(); }}
        className="w-full text-[10px] font-mono px-2 py-1 rounded bg-[var(--bg-primary)] border border-[var(--border)] text-[var(--text-primary)] placeholder:text-[var(--text-secondary)]/50"
      />
      <div className="flex gap-1.5">
        <button
          onClick={onSubmit}
          disabled={busy || !newKey.trim()}
          className="text-[10px] px-2 py-1 rounded bg-[var(--accent)] text-white hover:opacity-90 disabled:opacity-40"
        >
          {busy ? 'Adding…' : 'Add'}
        </button>
        <button onClick={onCancel} className="text-[10px] px-2 py-1 text-[var(--text-secondary)] hover:text-[var(--text-primary)]">
          Cancel
        </button>
      </div>
      {error && <div className="text-[9px] text-red-400">{error}</div>}
      {status && !error && <div className="text-[9px] text-emerald-400">{status}</div>}
    </div>
  );
}
