"use client";

import { useCallback, useEffect, useState } from "react";
import { useBrand } from "@/context/brand-context";
import { Globe, Plus, Send, Trash2, Loader2, Check, UserPlus, ExternalLink, X, ChevronDown, Image as ImageIcon, ShieldCheck, ShieldAlert, Mail } from "lucide-react";
import { PAGE_DEFS, buildDefaultConfig, resolvePortalConfig, type PortalConfig } from "@/lib/portal-config";
import FeatureGate from "@/components/plan/feature-gate";

interface Portal {
  id: string;
  isEnabled: boolean;
  agencyName: string | null;
  agencyLogo: string | null;
  primaryColor: string | null;
  accentColor: string | null;
  allowedPages: string[];
  portalConfig: PortalConfig | null;
  hideSourceBranding: boolean;
  customDomain: string | null;
  customDomainStatus: "pending" | "verified" | "failed" | null;
  customDomainVerifiedAt: string | null;
  customDomainDnsTarget: string | null;
  replyToEmail: string | null;
  supportEmail: string | null;
  _count?: { clientUsers: number };
}
interface ClientUser {
  id: string;
  email: string;
  name: string | null;
  role: string;
  lastLoginAt: string | null;
  isActive: boolean;
  status: "pending" | "active" | "disabled" | "revoked";
  invitedAt: string | null;
  pageOverrides: PortalConfig | null;
}

// Client Portal is an Agency+ feature. FeatureGate wrapper catches
// direct navigations that bypass the sidebar's locked-click handler.
export default function ClientPortalSetupPageGated() {
  return (
    <FeatureGate feature="client_portal">
      <ClientPortalSetupPage />
    </FeatureGate>
  );
}

function ClientPortalSetupPage() {
  const { brand } = useBrand();
  const [portal, setPortal] = useState<Portal | null>(null);
  const [users, setUsers] = useState<ClientUser[]>([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [inviteOpen, setInviteOpen] = useState(false);
  const [inviteEmail, setInviteEmail] = useState("");
  const [inviteName, setInviteName] = useState("");
  const [inviteRole, setInviteRole] = useState("viewer");
  const [inviting, setInviting] = useState(false);
  // Per-client page-visibility override. null = use portal default.
  // Seeded on modal open (create uses portal default as a starting
  // point; edit uses the user's existing overrides if any).
  const [inviteOverride, setInviteOverride] = useState<PortalConfig | null>(null);
  // Which client we're editing; null when the modal is in create mode.
  const [editingUser, setEditingUser] = useState<ClientUser | null>(null);
  const [toast, setToast] = useState<string | null>(null);

  // Form state
  const [enabled, setEnabled] = useState(false);
  const [agencyName, setAgencyName] = useState("");
  const [agencyLogo, setAgencyLogo] = useState("");
  const [primaryColor, setPrimaryColor] = useState("#EF4444");
  const [accentColor, setAccentColor] = useState("#10B981");
  const [pConfig, setPConfig] = useState<PortalConfig>(buildDefaultConfig());
  const [hideBranding, setHideBranding] = useState(true);
  const [customDomain, setCustomDomain] = useState("");
  const [replyToEmail, setReplyToEmail] = useState("");
  const [supportEmail, setSupportEmail] = useState("");
  // Separate UI state for the logo preview; logoBroken flips to true
  // when the <img>'s onError fires so we can surface an inline warning.
  const [logoBroken, setLogoBroken] = useState(false);

  const show = (msg: string) => { setToast(msg); setTimeout(() => setToast(null), 2000); };

  const load = useCallback(async () => {
    if (!brand.id) { setLoading(false); return; }
    setLoading(true);
    const cfgRes = await fetch(`/api/client-portal/config?brandId=${brand.id}`);
    if (cfgRes.ok) {
      const { portal: p } = await cfgRes.json();
      if (p) {
        setPortal(p);
        setEnabled(p.isEnabled);
        // Default the agency name to the brand name on first load so
        // the field reads as "pre-populated" rather than "loading…".
        setAgencyName(p.agencyName || brand.name || "");
        setAgencyLogo(p.agencyLogo || "");
        setPrimaryColor(p.primaryColor || "#EF4444");
        setAccentColor(p.accentColor || "#10B981");
        setPConfig(resolvePortalConfig(p.portalConfig, p.allowedPages));
        setHideBranding(p.hideSourceBranding);
        setCustomDomain(p.customDomain || "");
        setReplyToEmail(p.replyToEmail || "");
        setSupportEmail(p.supportEmail || "");
        const usersRes = await fetch(`/api/client-portal/users?portalId=${p.id}`);
        if (usersRes.ok) { const d = await usersRes.json(); setUsers(d.users || []); }
      } else {
        // No portal yet — seed the form with sensible defaults so the
        // first save actually persists meaningful data.
        setAgencyName(brand.name || "");
      }
    }
    setLoading(false);
  }, [brand.id, brand.name]);
  useEffect(() => { load(); }, [load]);
  // Reset the broken-logo flag whenever the URL changes so a fresh
  // URL gets a fresh chance to load.
  useEffect(() => { setLogoBroken(false); }, [agencyLogo]);

  async function save() {
    setSaving(true);
    try {
      const res = await fetch("/api/client-portal/setup", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          brandId: brand.id,
          isEnabled: enabled,
          agencyName,
          agencyLogo,
          primaryColor,
          accentColor,
          portalConfig: pConfig,
          hideSourceBranding: hideBranding,
          customDomain,
          replyToEmail,
          supportEmail,
        }),
      });
      if (res.ok) {
        const { portal: p } = await res.json();
        setPortal(p);
        show("Configuration saved");
      } else {
        const d = await res.json().catch(() => ({}));
        show(d.error || "Save failed");
      }
    } catch {
      show("Save failed — network error");
    } finally {
      setSaving(false);
    }
  }

  function openInviteModal(existing?: ClientUser) {
    if (existing) {
      setEditingUser(existing);
      setInviteEmail(existing.email);
      setInviteName(existing.name || "");
      setInviteRole(existing.role || "viewer");
      // Edit mode: pre-populate overrides from the user, falling back
      // to the portal's default so the checkboxes reflect the user's
      // current effective visibility rather than showing "nothing on".
      setInviteOverride(existing.pageOverrides ?? null);
    } else {
      setEditingUser(null);
      setInviteEmail("");
      setInviteName("");
      setInviteRole("viewer");
      setInviteOverride(null);
    }
    setInviteOpen(true);
  }

  async function invite() {
    if (!portal || !inviteEmail.trim()) return;
    setInviting(true);
    try {
      // pageOverrides: null = clear (use portal default); object = write
      // as per-user override. We only send the full object when the
      // agency has actually diverged from the default; matching exactly
      // sends null so the user falls back to the portal default.
      const overridePayload = inviteOverride && !pageConfigsEqual(inviteOverride, pConfig) ? inviteOverride : null;
      const res = await fetch("/api/client-portal/users", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          portalId: portal.id,
          email: inviteEmail.trim(),
          name: inviteName.trim() || null,
          role: inviteRole,
          pageOverrides: overridePayload,
        }),
      });
      if (res.ok) {
        if (!editingUser) {
          // Only send a magic link on create — edits shouldn't spam
          // the user with a fresh link every time the agency tweaks
          // their role or page visibility.
          await fetch("/api/client-portal/auth/send-magic-link", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ email: inviteEmail.trim(), brandId: brand.id }),
          });
        }
        setInviteOpen(false);
        show(editingUser ? "Client updated" : "Invite sent");
        await load();
      } else {
        const d = await res.json().catch(() => ({}));
        show(d.error || "Failed");
      }
    } catch {
      show("Failed — network error");
    } finally {
      setInviting(false);
    }
  }

  function toggleOverridePage(pageId: string) {
    setInviteOverride((prev) => {
      const base = prev ?? clonePortalConfig(pConfig);
      const cur = base.pages[pageId] ?? { enabled: false };
      return {
        pages: { ...base.pages, [pageId]: { ...cur, enabled: !cur.enabled } },
      };
    });
  }
  function clearOverride() { setInviteOverride(null); }
  const usingOverride = inviteOverride != null && !pageConfigsEqual(inviteOverride, pConfig);

  async function resendLink(email: string) {
    await fetch("/api/client-portal/auth/send-magic-link", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, brandId: brand.id }) });
    show("Magic link sent!");
  }

  async function deactivate(userId: string) {
    if (!confirm("Deactivate this client user?")) return;
    await fetch(`/api/client-portal/users/${userId}`, { method: "DELETE" });
    await load();
  }

  if (loading) return <div className="flex items-center justify-center h-64"><div className="w-8 h-8 border-4 border-brand-100 border-t-brand-600 rounded-full animate-spin" /></div>;

  return (
    <div className="space-y-6 max-w-4xl">
      <div className="flex items-start justify-between">
        <div>
          <h1 className="text-2xl font-bold text-slate-900">Client Portal</h1>
          <p className="text-slate-500 text-sm mt-1">White-label portal for {brand.name} clients</p>
        </div>
        {portal?.isEnabled && (
          <a href={`/client/${brand.id}/login`} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1.5 px-4 py-2 rounded-lg border border-slate-200 text-xs font-semibold text-slate-700 hover:bg-slate-50">
            <ExternalLink className="w-3.5 h-3.5" /> Preview Portal
          </a>
        )}
      </div>

      {/* Config */}
      <div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
        <div className="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
          <div className="flex items-center gap-2">
            <Globe className="w-4 h-4 text-brand-600" />
            <h2 className="font-bold text-slate-800 text-sm">Portal Configuration</h2>
          </div>
          <label className="flex items-center gap-2">
            <span className="text-xs font-semibold text-slate-600">{enabled ? "Enabled" : "Disabled"}</span>
            <button onClick={() => setEnabled(!enabled)} className={`relative w-9 h-5 rounded-full transition-colors ${enabled ? "bg-brand-600" : "bg-slate-200"}`}>
              <span className={`absolute top-0.5 ${enabled ? "left-4" : "left-0.5"} w-4 h-4 bg-white rounded-full shadow-sm transition-all`} />
            </button>
          </label>
        </div>
        <div className="px-6 py-5 space-y-5">
          <div className="grid grid-cols-2 gap-4">
            <div>
              <label className="block text-[11px] font-semibold text-slate-600 mb-1">Agency Name</label>
              <input
                type="text"
                value={agencyName}
                onChange={(e) => setAgencyName(e.target.value)}
                placeholder={brand.name || "Your agency"}
                className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg"
              />
              <p className="text-[10px] text-slate-400 mt-1">Shown in the portal header and as the From name on client emails.</p>
            </div>
            <div>
              <label className="block text-[11px] font-semibold text-slate-600 mb-1">Agency Logo URL</label>
              <div className="flex gap-2 items-start">
                <div className="flex-1">
                  <input
                    type="url"
                    value={agencyLogo}
                    onChange={(e) => setAgencyLogo(e.target.value)}
                    placeholder="https://yoursite.com/logo.png"
                    className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg"
                  />
                  {agencyLogo && logoBroken && (
                    <p className="text-[10px] text-red-600 mt-1">Couldn&apos;t load that image — check the URL.</p>
                  )}
                </div>
                {/* Preview — shows the actual logo at a realistic
                    portal-header size so the agency can eyeball
                    aspect ratio and contrast before saving. */}
                <div className="w-16 h-16 shrink-0 rounded-lg border border-slate-200 bg-slate-50 flex items-center justify-center overflow-hidden">
                  {agencyLogo && !logoBroken ? (
                    /* eslint-disable-next-line @next/next/no-img-element */
                    <img
                      src={agencyLogo}
                      alt="Logo preview"
                      className="max-w-full max-h-full object-contain"
                      onError={() => setLogoBroken(true)}
                      onLoad={() => setLogoBroken(false)}
                    />
                  ) : (
                    <ImageIcon className="w-5 h-5 text-slate-300" />
                  )}
                </div>
              </div>
            </div>
          </div>
          <div className="grid grid-cols-2 gap-4">
            <div><label className="block text-[11px] font-semibold text-slate-600 mb-1">Primary Color</label><div className="flex gap-2"><input type="color" value={primaryColor} onChange={(e) => setPrimaryColor(e.target.value)} className="w-10 h-9 rounded-lg border border-slate-200" /><input type="text" value={primaryColor} onChange={(e) => setPrimaryColor(e.target.value)} className="flex-1 px-3 py-2 text-xs font-mono border border-slate-200 rounded-lg" /></div></div>
            <div><label className="block text-[11px] font-semibold text-slate-600 mb-1">Accent Color</label><div className="flex gap-2"><input type="color" value={accentColor} onChange={(e) => setAccentColor(e.target.value)} className="w-10 h-9 rounded-lg border border-slate-200" /><input type="text" value={accentColor} onChange={(e) => setAccentColor(e.target.value)} className="flex-1 px-3 py-2 text-xs font-mono border border-slate-200 rounded-lg" /></div></div>
          </div>

          {/* Custom domain — allows agencies to host the client portal
              on their own subdomain (e.g. seo.growthagency.com). The
              verification state drives a chip next to the field; actual
              DNS verification runs server-side in a follow-up commit.
              For now the save persists the domain and resets the state
              to "pending" so the agency sees the DNS target. */}
          <div>
            <label className="block text-[11px] font-semibold text-slate-600 mb-1">Custom Domain (optional)</label>
            <div className="flex gap-2 items-center">
              <input
                type="text"
                value={customDomain}
                onChange={(e) => setCustomDomain(e.target.value)}
                placeholder="seo.youragency.com"
                className="flex-1 px-3 py-2 text-sm border border-slate-200 rounded-lg font-mono"
              />
              <CustomDomainStatusChip portal={portal} />
            </div>
            {customDomain && portal?.customDomainStatus !== "verified" && (
              <div className="mt-2 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-[11px] text-amber-800">
                <p className="font-semibold mb-0.5">DNS setup required</p>
                <p>
                  Add a CNAME record for <span className="font-mono">{customDomain}</span> pointing to{" "}
                  <span className="font-mono font-semibold">{portal?.customDomainDnsTarget || "portal.meseoapp.com"}</span>.
                  We&apos;ll verify it automatically after you save.
                </p>
              </div>
            )}
          </div>

          {/* White-label email addressing. When replyToEmail is set,
              client emails (invites + scheduled reports) will be sent
              with the agency's name as the From header and route
              replies to this inbox. supportEmail appears in footers. */}
          <div>
            <label className="block text-[11px] font-semibold text-slate-600 mb-1 flex items-center gap-1.5">
              <Mail className="w-3 h-3" /> White-label Email (optional)
            </label>
            <div className="grid grid-cols-2 gap-3">
              <div>
                <input
                  type="email"
                  value={replyToEmail}
                  onChange={(e) => setReplyToEmail(e.target.value)}
                  placeholder="Reply-to: hello@youragency.com"
                  className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg"
                />
                <p className="text-[10px] text-slate-400 mt-1">Client replies route here.</p>
              </div>
              <div>
                <input
                  type="email"
                  value={supportEmail}
                  onChange={(e) => setSupportEmail(e.target.value)}
                  placeholder="Support: support@youragency.com"
                  className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg"
                />
                <p className="text-[10px] text-slate-400 mt-1">Shown in email footers.</p>
              </div>
            </div>
          </div>
          <div>
            <label className="block text-[11px] font-semibold text-slate-600 mb-2">Client Pages &amp; Sections</label>
            <div className="space-y-1.5">
              {PAGE_DEFS.map((def) => {
                const pc = pConfig.pages[def.id] || { enabled: false };
                const togglePageEnabled = () => setPConfig((prev) => ({ pages: { ...prev.pages, [def.id]: { ...prev.pages[def.id], enabled: !pc.enabled } } }));
                const toggleSection = (sid: string) => setPConfig((prev) => {
                  const cur = prev.pages[def.id] || { enabled: true, sections: {} };
                  return { pages: { ...prev.pages, [def.id]: { ...cur, sections: { ...cur.sections, [sid]: !(cur.sections?.[sid] ?? true) } } } };
                });
                return (
                  <div key={def.id} className={`rounded-lg border ${pc.enabled ? "border-brand-200 bg-brand-50/30" : "border-slate-200"}`}>
                    <label className="flex items-center gap-2.5 px-3 py-2.5 cursor-pointer">
                      <input type="checkbox" checked={pc.enabled} onChange={togglePageEnabled} className="rounded" />
                      <div className="flex-1 min-w-0">
                        <span className="text-xs font-semibold text-slate-800">{def.label}</span>
                        <p className="text-[10px] text-slate-500">{def.desc}</p>
                      </div>
                      {def.sections && def.sections.length > 0 && pc.enabled && <ChevronDown className="w-3.5 h-3.5 text-slate-400" />}
                    </label>
                    {def.sections && def.sections.length > 0 && pc.enabled && (
                      <div className="px-3 pb-2.5 pl-9 space-y-1">
                        {def.sections.map((s) => (
                          <label key={s.id} className="flex items-center gap-2 cursor-pointer">
                            <input type="checkbox" checked={pc.sections?.[s.id] !== false} onChange={() => toggleSection(s.id)} className="rounded" />
                            <span className="text-[11px] text-slate-600">{s.label}</span>
                          </label>
                        ))}
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          </div>
          <label className="flex items-center justify-between"><span className="text-xs font-semibold text-slate-600">Hide &quot;Powered by meSEO&quot; branding</span>
            <button onClick={() => setHideBranding(!hideBranding)} className={`relative w-9 h-5 rounded-full transition-colors ${hideBranding ? "bg-brand-600" : "bg-slate-200"}`}><span className={`absolute top-0.5 ${hideBranding ? "left-4" : "left-0.5"} w-4 h-4 bg-white rounded-full shadow-sm transition-all`} /></button>
          </label>
          <button onClick={save} disabled={saving} className="flex items-center gap-1.5 px-5 py-2 bg-brand-600 text-white text-xs font-semibold rounded-lg hover:bg-brand-700 disabled:opacity-60">
            {saving ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Check className="w-3.5 h-3.5" />} Save Configuration
          </button>
        </div>
      </div>

      {/* Users */}
      {portal && (
        <div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
          <div className="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
            <h2 className="font-bold text-slate-800 text-sm">Client Users</h2>
            <button onClick={() => openInviteModal()} className="flex items-center gap-1.5 px-3 py-1.5 bg-brand-600 text-white text-[11px] font-semibold rounded-lg hover:bg-brand-700"><UserPlus className="w-3 h-3" /> Invite Client</button>
          </div>
          {users.length === 0 ? (
            <div className="px-6 py-8 text-center text-sm text-slate-400">No client users yet. Invite your first client above.</div>
          ) : (
            <table className="w-full text-sm">
              <thead><tr className="border-b border-slate-100 bg-slate-50/80 text-[10px] font-bold uppercase tracking-widest text-slate-400">
                <th className="px-4 py-2.5 text-left">Name</th>
                <th className="px-4 py-2.5 text-left">Email</th>
                <th className="px-4 py-2.5">Role</th>
                <th className="px-4 py-2.5">Access</th>
                <th className="px-4 py-2.5">Last Login</th>
                <th className="px-4 py-2.5">Status</th>
                <th className="px-4 py-2.5" />
              </tr></thead>
              <tbody>
                {users.map((u) => {
                  const statusMeta = USER_STATUS_META[u.status] ?? USER_STATUS_META.pending;
                  const hasOverride = u.pageOverrides != null;
                  return (
                    <tr key={u.id} className="border-b border-slate-100 hover:bg-slate-50">
                      <td className="px-4 py-2.5 font-medium text-slate-900">{u.name || "—"}</td>
                      <td className="px-4 py-2.5 text-slate-600">{u.email}</td>
                      <td className="px-4 py-2.5 text-center"><span className="text-[10px] font-bold uppercase tracking-widest text-slate-500">{u.role}</span></td>
                      <td className="px-4 py-2.5 text-center">
                        {hasOverride ? (
                          <span className="text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded border border-indigo-200 bg-indigo-50 text-indigo-700" title="This client has a custom page-visibility override">
                            Custom
                          </span>
                        ) : (
                          <span className="text-[10px] text-slate-400">Default</span>
                        )}
                      </td>
                      <td className="px-4 py-2.5 text-center text-xs text-slate-500">{u.lastLoginAt ? new Date(u.lastLoginAt).toLocaleDateString("en-US", { month: "short", day: "numeric" }) : "Never"}</td>
                      <td className="px-4 py-2.5 text-center">
                        <span className={`text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded border ${statusMeta.chip}`}>
                          {statusMeta.label}
                        </span>
                      </td>
                      <td className="px-4 py-2.5">
                        <div className="flex items-center gap-1 justify-end">
                          <button onClick={() => openInviteModal(u)} title="Edit" className="w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:bg-slate-100 hover:text-brand-600"><UserPlus className="w-3 h-3" /></button>
                          <button onClick={() => resendLink(u.email)} title="Resend magic link" className="w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:bg-slate-100 hover:text-brand-600"><Send className="w-3 h-3" /></button>
                          <button onClick={() => deactivate(u.id)} title="Revoke" className="w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:bg-red-50 hover:text-red-600"><Trash2 className="w-3 h-3" /></button>
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )}
        </div>
      )}

      {/* Invite / edit modal */}
      {inviteOpen && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/60 p-4" onClick={() => setInviteOpen(false)}>
          <div className="bg-white rounded-2xl shadow-2xl w-full max-w-md max-h-[90vh] flex flex-col overflow-hidden" onClick={(e) => e.stopPropagation()}>
            <div className="flex items-center justify-between px-5 py-4 border-b border-slate-200">
              <h2 className="font-bold text-slate-900">{editingUser ? "Edit Client" : "Invite Client"}</h2>
              <button onClick={() => setInviteOpen(false)} className="p-1.5 rounded-lg hover:bg-slate-100 text-slate-500"><X className="w-4 h-4" /></button>
            </div>
            <div className="flex-1 overflow-auto p-5 space-y-3">
              <div>
                <label className="block text-[11px] font-semibold text-slate-600 mb-1">Email</label>
                <input
                  type="email"
                  value={inviteEmail}
                  onChange={(e) => setInviteEmail(e.target.value)}
                  disabled={!!editingUser}
                  className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg disabled:bg-slate-50 disabled:text-slate-500"
                />
              </div>
              <div><label className="block text-[11px] font-semibold text-slate-600 mb-1">Name</label><input type="text" value={inviteName} onChange={(e) => setInviteName(e.target.value)} className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg" /></div>
              <div><label className="block text-[11px] font-semibold text-slate-600 mb-1">Role</label><select value={inviteRole} onChange={(e) => setInviteRole(e.target.value)} className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg bg-white"><option value="viewer">Viewer</option><option value="editor">Editor</option></select></div>

              {/* Per-client page-visibility override. Starts off ==
                  the portal default; checking/unchecking a page
                  automatically flips this client into "custom" mode.
                  "Use portal default" resets to null so the user
                  inherits future portal-level changes. */}
              <div className="pt-2 border-t border-slate-100">
                <div className="flex items-center justify-between mb-1.5">
                  <label className="block text-[11px] font-semibold text-slate-600">Page Access</label>
                  {usingOverride ? (
                    <button onClick={clearOverride} className="text-[10px] font-semibold text-brand-600 hover:text-brand-700 underline">
                      Use portal default
                    </button>
                  ) : (
                    <span className="text-[10px] text-slate-400">Using portal default</span>
                  )}
                </div>
                <div className="rounded-lg border border-slate-200 max-h-48 overflow-y-auto">
                  {PAGE_DEFS.map((def) => {
                    const effective = inviteOverride ?? pConfig;
                    const pc = effective.pages[def.id] ?? { enabled: false };
                    return (
                      <label key={def.id} className="flex items-center gap-2.5 px-3 py-2 cursor-pointer hover:bg-slate-50 border-b border-slate-100 last:border-b-0">
                        <input type="checkbox" checked={pc.enabled} onChange={() => toggleOverridePage(def.id)} className="rounded" />
                        <span className="text-xs font-medium text-slate-700 flex-1">{def.label}</span>
                      </label>
                    );
                  })}
                </div>
                {usingOverride && (
                  <p className="text-[10px] text-indigo-600 mt-1.5">
                    This client has a custom page-visibility override.
                  </p>
                )}
              </div>
            </div>
            <div className="flex justify-end gap-2 px-5 py-3 border-t border-slate-200 bg-slate-50">
              <button onClick={() => setInviteOpen(false)} className="px-4 py-2 text-xs font-semibold text-slate-600">Cancel</button>
              <button onClick={invite} disabled={inviting || !inviteEmail.trim()} className="flex items-center gap-1.5 px-4 py-2 bg-brand-600 text-white text-xs font-semibold rounded-lg disabled:opacity-60">
                {inviting ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Plus className="w-3.5 h-3.5" />}
                {editingUser ? "Save Changes" : "Send Invite"}
              </button>
            </div>
          </div>
        </div>
      )}

      {toast && <div className="fixed top-4 right-4 z-50 px-4 py-2 rounded-lg bg-slate-900 text-white text-xs font-semibold shadow-lg">{toast}</div>}
    </div>
  );
}

function clonePortalConfig(c: PortalConfig): PortalConfig {
  return JSON.parse(JSON.stringify(c)) as PortalConfig;
}

/** Deep-equality check for PortalConfig. Used to decide whether a
 *  per-client override actually diverges from the portal default —
 *  if it matches exactly we persist null (= "follow the default") so
 *  the user inherits future portal-level changes automatically. */
function pageConfigsEqual(a: PortalConfig, b: PortalConfig): boolean {
  return JSON.stringify(a) === JSON.stringify(b);
}

const USER_STATUS_META: Record<ClientUser["status"], { label: string; chip: string }> = {
  active:   { label: "Active",   chip: "bg-emerald-50 text-emerald-700 border-emerald-200" },
  pending:  { label: "Pending",  chip: "bg-amber-50 text-amber-700 border-amber-200" },
  disabled: { label: "Disabled", chip: "bg-slate-100 text-slate-600 border-slate-200" },
  revoked:  { label: "Revoked",  chip: "bg-red-50 text-red-600 border-red-200" },
};

function CustomDomainStatusChip({ portal }: { portal: Portal | null }) {
  const status = portal?.customDomainStatus;
  if (!status || !portal?.customDomain) {
    return (
      <span className="text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded border border-slate-200 bg-slate-50 text-slate-500">
        Not configured
      </span>
    );
  }
  if (status === "verified") {
    return (
      <span className="inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded border border-emerald-200 bg-emerald-50 text-emerald-700">
        <ShieldCheck className="w-3 h-3" /> Verified
      </span>
    );
  }
  if (status === "failed") {
    return (
      <span className="inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded border border-red-200 bg-red-50 text-red-700">
        <ShieldAlert className="w-3 h-3" /> Failed
      </span>
    );
  }
  return (
    <span className="inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded border border-amber-200 bg-amber-50 text-amber-700">
      <Loader2 className="w-3 h-3 animate-spin" /> Pending
    </span>
  );
}
