// Account settings page — DynamoDB-backed via API Gateway

// ── API layer ─────────────────────────────────────────────────────────────────
const API_BASE       = 'https://r5vefiej3l.execute-api.eu-central-1.amazonaws.com/prod';
const COGNITO_CLIENT = '7nffjheavp985k7b4bgug45jf4';

function getToken() {
  const last = localStorage.getItem(`CognitoIdentityServiceProvider.${COGNITO_CLIENT}.LastAuthUser`);
  if (!last) return null;
  return localStorage.getItem(`CognitoIdentityServiceProvider.${COGNITO_CLIENT}.${last}.idToken`);
}

function _isCognitoToken(token) {
  try {
    const payload = JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')));
    return payload.token_use === 'access' && String(payload.iss || '').includes('cognito-idp');
  } catch { return false; }
}

// Returns true for email/password Cognito users; false for GitHub OAuth users
function isEmailPasswordUser() {
  const t = localStorage.getItem('fossyl_access_token');
  return !!t && _isCognitoToken(t);
}

// Use the amazon-cognito-identity-js SDK (loaded via CDN) for all Cognito operations.
// Raw fetch to the Cognito endpoint fails because the SDK handles session refresh,
// secret hash computation, and other auth details that plain fetch misses.
function _getCognitoSDKUser() {
  if (!window.AmazonCognitoIdentity) return null;
  const pool = new window.AmazonCognitoIdentity.CognitoUserPool({
    UserPoolId: 'eu-central-1_IDzeVSllu',
    ClientId:   COGNITO_CLIENT,
  });
  return pool.getCurrentUser();
}

function withCognitoSession(fn) {
  return new Promise((resolve, reject) => {
    const user = _getCognitoSDKUser();
    if (!user) return reject(new Error('Not signed in — please sign out and back in'));
    user.getSession((err, session) => {
      if (err || !session?.isValid()) {
        return reject(err || new Error('Session expired — please sign in again'));
      }
      fn(user, resolve, reject);
    });
  });
}

async function api(method, path, body) {
  const token = getToken();
  const res = await fetch(`${API_BASE}${path}`, {
    method,
    headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) },
    ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
  return data;
}

// ── localStorage helpers (sidebar cache only) ─────────────────────────────────
function storageGet(key, fallback) {
  try { const v = localStorage.getItem(key); return v ? JSON.parse(v) : fallback; } catch { return fallback; }
}
function storageSet(key, val) { try { localStorage.setItem(key, JSON.stringify(val)); } catch {} }
function initials(name) {
  return (name || '?').split(' ').filter(Boolean).map(w => w[0]).join('').slice(0, 2).toUpperCase();
}

// ── Shared UI primitives ──────────────────────────────────────────────────────
function Setting({ label, help, children }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '260px 1fr', gap: 32, padding: '20px 0', borderBottom: '1px solid var(--line)' }}>
      <div>
        <div style={{ fontSize: 13.5, fontWeight: 500 }}>{label}</div>
        {help && <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 4, lineHeight: 1.5 }}>{help}</div>}
      </div>
      <div>{children}</div>
    </div>
  );
}

function SegmentControl({ options, value, onChange }) {
  return (
    <div className="row g-0" style={{ border: '1px solid var(--line-2)', borderRadius: 3, overflow: 'hidden', width: 'fit-content' }}>
      {options.map((opt, i) => (
        <button key={opt} onClick={() => onChange(opt)} style={{
          padding: '8px 18px', background: value === opt ? 'var(--bg-2)' : 'transparent',
          border: 0, borderRight: i < options.length - 1 ? '1px solid var(--line-2)' : 0,
          color: value === opt ? 'var(--ink)' : 'var(--muted)',
          fontFamily: 'var(--mono)', fontSize: 11, cursor: 'pointer', transition: 'background 120ms, color 120ms',
        }}>{opt}</button>
      ))}
    </div>
  );
}

function Modal({ children, onClose, width = 440 }) {
  useEffect(() => {
    const fn = e => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', fn);
    return () => window.removeEventListener('keydown', fn);
  }, [onClose]);
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.65)', display: 'grid', placeItems: 'center', zIndex: 100 }} onClick={onClose}>
      <div onClick={e => e.stopPropagation()} style={{ width, background: 'var(--bg)', border: '1px solid var(--line-2)', borderRadius: 3, padding: 28, boxShadow: '0 20px 60px rgba(0,0,0,0.5)' }}>
        <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 4 }}>
          <button onClick={onClose} style={{ background: 'none', border: 0, color: 'var(--muted)', cursor: 'pointer', fontSize: 16, padding: '0 0 8px 8px' }}>✕</button>
        </div>
        {children}
      </div>
    </div>
  );
}

function SaveBar({ onSave, onDiscard, saving, dirty }) {
  return (
    <div className="row jc-e g-8" style={{ marginTop: 24 }}>
      <button className="btn" onClick={onDiscard} disabled={!dirty || saving} style={{ opacity: dirty ? 1 : 0.4 }}>Discard</button>
      <button className="btn btn-primary" onClick={onSave} disabled={!dirty || saving} style={{ opacity: dirty ? 1 : 0.4 }}>
        {saving ? 'Saving…' : 'Save changes'}
      </button>
    </div>
  );
}

// ── AccountPage ───────────────────────────────────────────────────────────────
function AccountPage() {
  const { go }     = useNav();
  const { toast }  = useToast();
  const [tab,      setTab]       = useState('profile');
  const [profile,  setProfile]   = useState(null);
  const [workspace,setWorkspace] = useState(null);
  const [members,  setMembers]   = useState(null);
  const [loading,  setLoading]   = useState(true);
  const [apiError, setApiError]  = useState(false);

  useEffect(() => {
    // Profile must come first — it bootstraps the workspace on first login
    api('GET', '/account/profile')
      .then(p => {
        setProfile(p);
        _sidebarProfile = p;
        return Promise.all([
          api('GET', '/account/workspace'),
          api('GET', '/account/members'),
        ]);
      })
      .then(([ws, mem]) => {
        const memberList = mem.members || [];
        setWorkspace(ws);
        _sidebarWorkspace = ws;
        setMembers(memberList);
        _sbMembers = memberList;
        setLoading(false);
      })
      .catch(err => {
        console.warn('Account API unavailable:', err.message);
        setProfile({ display_name: '', email: '', role_title: '', timezone: 'Europe/Berlin (CET · UTC+01:00)', member_since: '', workspace_id: '' });
        setWorkspace({ org_name: '', workspace_slug: '', industry: 'Other', framework: 'Plone 4', seat_limit: 5, plan: 'field' });
        setMembers([]);
        setApiError(true);
        setLoading(false);
      });
  }, []);

  if (loading) return (
    <div style={{ minHeight: '100vh', display: 'grid', gridTemplateColumns: '240px 1fr', background: 'var(--bg)' }}>
      <AccountSidebar current="account" go={go}/>
      <div style={{ display: 'grid', placeItems: 'center' }}>
        <div className="mono" style={{ fontSize: 12, color: 'var(--dim)' }}>loading account…</div>
      </div>
    </div>
  );

  const TABS = [
    ['profile','Profile'],['team','Team & seats'],['plan','Plan'],['org','Organisation'],
    ['security','Security'],['data','Data handling'],['api','API & tokens'],
    ['notifications','Notifications'],['danger','Danger zone'],
  ];

  return (
    <div style={{ minHeight: '100vh', display: 'grid', gridTemplateColumns: '240px 1fr', background: 'var(--bg)' }}>
      <AccountSidebar current="account" go={go}/>
      <div style={{ overflow: 'auto' }}>
        <AccountTopBar crumb={['settings', tab]}/>
        <div style={{ padding: '32px', maxWidth: 1080 }}>
          {apiError && (
            <div style={{ padding: '10px 16px', border: '1px solid var(--rust-dim)', borderRadius: 3, background: 'rgba(186,90,50,0.06)', marginBottom: 24, fontSize: 12, color: 'var(--rust)', fontFamily: 'var(--mono)' }}>
              ⚠ API unreachable — changes will not persist until the backend is available.
            </div>
          )}
          <SectionHeader number="A1" eyebrow="Settings" title="Account."
            kicker="Your profile, team, and data-handling preferences."/>

          <div className="row" style={{ marginTop: 32, borderBottom: '1px solid var(--line)', gap: 0, flexWrap: 'wrap' }}>
            {TABS.map(([id, label]) => (
              <button key={id} onClick={() => setTab(id)} style={{
                background: 'transparent', border: 0, padding: '12px 18px',
                color: id === 'danger' ? (tab===id ? 'var(--rust)' : 'var(--rust-dim)') : (tab===id ? 'var(--ink)' : 'var(--muted)'),
                borderBottom: tab === id ? `1px solid ${id==='danger' ? 'var(--rust)' : 'var(--bone)'}` : '1px solid transparent',
                marginBottom: -1, fontSize: 13, fontFamily: 'var(--sans)', fontWeight: 500,
                cursor: 'pointer', letterSpacing: '-0.005em',
              }}>{label}</button>
            ))}
          </div>

          <div style={{ marginTop: 32 }}>
            {tab === 'profile'       && <TabProfile       initialProfile={profile}    onProfileSaved={p => { setProfile(p); _sidebarProfile = p; }}/>}
            {tab === 'team'          && <TabTeam           initialMembers={members}    workspace={workspace} onMembersChange={m => { setMembers(m); _sbMembers = m; }}/>}
            {tab === 'plan'          && <TabPlan           go={go} workspace={workspace}/>}
            {tab === 'org'           && <TabOrg            initialWorkspace={workspace} onWorkspaceSaved={ws => { setWorkspace(ws); _sidebarWorkspace = ws; }}/>}
            {tab === 'security'      && <TabSecurity/>}
            {tab === 'data'          && <TabData profile={profile}/>}
            {tab === 'api'           && <TabApi/>}
            {tab === 'notifications' && <TabNotifications/>}
            {tab === 'danger'        && <TabDanger         go={go} members={members}/>}
          </div>
        </div>
      </div>
    </div>
  );
}

// ── Profile tab ───────────────────────────────────────────────────────────────
function TabProfile({ initialProfile, onProfileSaved }) {
  const { toast }  = useToast();
  const [saved,    setSaved]   = useState(initialProfile);
  const [form,     setForm]    = useState({ ...initialProfile });
  const [saving,   setSaving]  = useState(false);
  const dirty = JSON.stringify(form) !== JSON.stringify(saved);
  const set   = k => e => setForm(f => ({ ...f, [k]: e.target.value }));

  const onSave = async () => {
    setSaving(true);
    try {
      await api('PUT', '/account/profile', { display_name: form.display_name, role_title: form.role_title, timezone: form.timezone });
      const next = { ...form };
      setSaved(next);
      _sidebarProfile = next;
      onProfileSaved?.(next);
      toast('Profile saved', { tone: 'success' });
    } catch (e) { toast(e.message || 'Save failed', { tone: 'error' }); }
    finally { setSaving(false); }
  };
  const onDiscard = () => { setForm({ ...saved }); toast('Changes discarded'); };
  const av = initials(form.display_name);

  return (
    <div>
      <div className="row g-24 ai-c" style={{ padding: '8px 0 24px', borderBottom: '1px solid var(--line)' }}>
        <div style={{ width: 72, height: 72, borderRadius: '50%', background: 'var(--bg-2)', border: '1px solid var(--line-2)', display: 'grid', placeItems: 'center', fontFamily: 'var(--serif)', fontSize: 28, color: 'var(--bone)', userSelect: 'none' }}>{av}</div>
        <div style={{ flex: 1 }}>
          <div className="serif" style={{ fontSize: 22 }}>{form.display_name || '—'}</div>
          <div className="mono" style={{ fontSize: 11.5, color: 'var(--muted)' }}>{form.email} · member since {form.member_since?.slice(0,10) || '—'}</div>
        </div>
        <button className="btn btn-sm" onClick={() => toast('Portrait upload — coming soon')}>Upload portrait</button>
      </div>
      <Setting label="Display name" help="Shown in report comments and activity log.">
        <input className="input" value={form.display_name} onChange={set('display_name')}/>
      </Setting>
      <Setting label="Work email" help="Used for sign-in and invoice notifications.">
        <input className="input mono" value={form.email} readOnly style={{ opacity: 0.6 }}/>
        <div className="mono" style={{ fontSize: 10.5, color: 'var(--dim)', marginTop: 6 }}>Email is managed by Cognito — contact support to change it.</div>
      </Setting>
      <Setting label="Role" help="How Fossyl labels your activity in team logs.">
        <input className="input" value={form.role_title} onChange={set('role_title')} placeholder="e.g. Engineering lead · Migration team"/>
      </Setting>
      <Setting label="Timezone" help="All timestamps in reports are rendered in this zone.">
        <select className="input" value={form.timezone} onChange={set('timezone')} style={{ appearance: 'none' }}>
          <option>Europe/Berlin (CET · UTC+01:00)</option>
          <option>Europe/London (GMT · UTC+00:00)</option>
          <option>America/New_York (EST · UTC-05:00)</option>
          <option>America/Los_Angeles (PST · UTC-08:00)</option>
          <option>Asia/Tokyo (JST · UTC+09:00)</option>
        </select>
      </Setting>
      <SaveBar onSave={onSave} onDiscard={onDiscard} saving={saving} dirty={dirty}/>
    </div>
  );
}

// ── Team tab ──────────────────────────────────────────────────────────────────
function TabTeam({ initialMembers, workspace, onMembersChange }) {
  const { toast }    = useToast();
  const { role: myRole } = useAuth();
  const canManage    = myRole === 'Owner' || myRole === 'Admin';
  const [list,       setList]       = useState(initialMembers || []);
  const [managing,   setManaging]   = useState(null);
  const [inviteOpen, setInviteOpen] = useState(false);
  const [defaultRole,setDefaultRole]= useState('Member');
  const [busy,       setBusy]       = useState(false);

  const totalSeats   = workspace?.seat_limit || 5;
  const activeCount  = list.filter(m => m.status === 'active').length;
  const invitedCount = list.filter(m => m.status === 'invited').length;
  const remaining    = Math.max(0, totalSeats - list.length);
  const sync = updated => { setList(updated); onMembersChange?.(updated); };

  const updateRole = async (email, role) => {
    setBusy(true);
    try {
      await api('PATCH', `/account/members/${encodeURIComponent(email)}`, { role });
      sync(list.map(m => m.email === email ? { ...m, role } : m));
      setManaging(prev => prev ? { ...prev, role } : null);
      toast(`Role updated to ${role}`, { tone: 'success' });
    } catch (e) { toast(e.message, { tone: 'error' }); }
    finally { setBusy(false); }
  };

  const remove = async email => {
    setBusy(true);
    try {
      await api('DELETE', `/account/members/${encodeURIComponent(email)}`);
      sync(list.filter(m => m.email !== email));
      toast('Member removed', { tone: 'success' });
      setManaging(null);
    } catch (e) { toast(e.message, { tone: 'error' }); }
    finally { setBusy(false); }
  };

  const invite = async (email, role) => {
    setBusy(true);
    try {
      const res = await api('POST', '/account/members', { email, role });
      sync([...list, { email, display_name: res.display_name || email.split('@')[0], role, status: 'invited', invited_at: new Date().toISOString() }]);
      toast(`Invite sent to ${email}`, { tone: 'success' });
      setInviteOpen(false);
    } catch (e) { toast(e.message, { tone: 'error' }); }
    finally { setBusy(false); }
  };

  return (
    <div>
      <div className="row jc-b ai-e" style={{ marginBottom: 16 }}>
        <div>
          <span className="caps">Team · {activeCount} active{invitedCount > 0 ? `, ${invitedCount} invited` : ''}</span>
          <div className="serif" style={{ fontSize: 22, marginTop: 4, letterSpacing: '-0.01em' }}>{totalSeats} seats included · {remaining} remaining</div>
        </div>
        {canManage && (
          <div className="row g-8">
            <button className="btn btn-sm" onClick={() => toast('Seat added — +€ 60 / mo on next invoice', { tone: 'success' })}>Add seat (€ 60)</button>
            <button className="btn btn-sm btn-primary" onClick={() => setInviteOpen(true)}>Invite member</button>
          </div>
        )}
      </div>
      <div className="panel">
        <table className="table">
          <thead><tr><th style={{ width: 44 }}></th><th>Name</th><th>Email</th><th>Role</th><th>Status</th><th>Since</th><th></th></tr></thead>
          <tbody>
            {list.map(m => (
              <tr key={m.email}>
                <td><div style={{ width: 26, height: 26, borderRadius: '50%', background: 'var(--bg-2)', border: '1px solid var(--line-2)', display: 'grid', placeItems: 'center', fontFamily: 'var(--serif)', fontSize: 12, color: 'var(--bone)' }}>{initials(m.display_name)}</div></td>
                <td style={{ color: 'var(--ink)', fontFamily: 'var(--sans)', fontSize: 13 }}>{m.display_name}</td>
                <td className="mono">{m.email}</td>
                <td><span className="badge">{m.role}</span></td>
                <td><span className={`badge ${m.status === 'active' ? 'safe' : 'warn'}`}>{m.status}</span></td>
                <td style={{ color: 'var(--muted)' }}>{(m.joined_at || m.invited_at || '').slice(0,10) || '—'}</td>
                <td>{canManage && m.role !== 'Owner' && <a className="bone mono" style={{ fontSize: 11, cursor: 'pointer' }} onClick={() => setManaging(m)}>manage →</a>}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <Setting label="Default role for new members" help="Applied to new invitations unless overridden.">
        <SegmentControl options={['Viewer', 'Member', 'Admin']} value={defaultRole} onChange={setDefaultRole}/>
      </Setting>

      {managing && (
        <Modal onClose={() => setManaging(null)}>
          <div className="caps" style={{ marginBottom: 16 }}>Manage member</div>
          <div className="row g-12 ai-c" style={{ marginBottom: 24 }}>
            <div style={{ width: 36, height: 36, borderRadius: '50%', background: 'var(--bg-2)', border: '1px solid var(--line-2)', display: 'grid', placeItems: 'center', fontFamily: 'var(--serif)', fontSize: 15, color: 'var(--bone)' }}>{initials(managing.display_name)}</div>
            <div><div style={{ fontSize: 14, fontWeight: 500 }}>{managing.display_name}</div><div className="mono" style={{ fontSize: 11, color: 'var(--muted)' }}>{managing.email}</div></div>
          </div>
          <div><div className="caps" style={{ marginBottom: 8 }}>Role</div>
            <SegmentControl options={['Viewer', 'Member', 'Admin']} value={managing.role} onChange={r => updateRole(managing.email, r)}/>
          </div>
          <div className="col g-8" style={{ marginTop: 24, paddingTop: 20, borderTop: '1px solid var(--line)' }}>
            {managing.status === 'invited' && <button className="btn" disabled={busy} onClick={() => { toast(`Invite resent to ${managing.email}`, { tone: 'success' }); setManaging(null); }}>Resend invite →</button>}
            <button className="btn" disabled={busy} style={{ borderColor: 'var(--rust-dim)', color: 'var(--rust)' }} onClick={() => remove(managing.email)}>
              {managing.status === 'invited' ? 'Cancel invite' : 'Remove member'}
            </button>
          </div>
        </Modal>
      )}
      {inviteOpen && <InviteModal defaultRole={defaultRole} onClose={() => setInviteOpen(false)} onInvite={invite} busy={busy}/>}
    </div>
  );
}

function InviteModal({ defaultRole, onClose, onInvite, busy }) {
  const [email, setEmail] = useState('');
  const [role,  setRole]  = useState(defaultRole);
  const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  return (
    <Modal onClose={onClose}>
      <div className="caps" style={{ marginBottom: 20 }}>Invite member</div>
      <div className="col g-16">
        <div>
          <span className="label" style={{ display: 'block', marginBottom: 6 }}>Email address</span>
          <input className="input mono" value={email} onChange={e => setEmail(e.target.value)} placeholder="colleague@yourdomain.com" autoFocus
            onKeyDown={e => e.key === 'Enter' && valid && !busy && onInvite(email, role)}/>
        </div>
        <div><span className="label" style={{ display: 'block', marginBottom: 8 }}>Role</span>
          <SegmentControl options={['Viewer', 'Member', 'Admin']} value={role} onChange={setRole}/>
        </div>
        <div className="row jc-e g-8" style={{ marginTop: 8 }}>
          <button className="btn" onClick={onClose}>Cancel</button>
          <button className="btn btn-primary" disabled={!valid || busy} style={{ opacity: valid ? 1 : 0.45 }} onClick={() => onInvite(email, role)}>
            {busy ? 'Sending…' : 'Send invite →'}
          </button>
        </div>
      </div>
    </Modal>
  );
}

// ── Organisation tab ──────────────────────────────────────────────────────────
function TabOrg({ initialWorkspace, onWorkspaceSaved }) {
  const { toast }  = useToast();
  const [saved,    setSaved]   = useState(initialWorkspace);
  const [form,     setForm]    = useState({ ...initialWorkspace });
  const [saving,   setSaving]  = useState(false);
  const dirty = JSON.stringify(form) !== JSON.stringify(saved);
  const set   = k => e => setForm(f => ({ ...f, [k]: e.target.value }));

  const onSave = async () => {
    setSaving(true);
    try {
      await api('PUT', '/account/workspace', { org_name: form.org_name, workspace_slug: form.workspace_slug, industry: form.industry, framework: form.framework });
      const next = { ...form }; setSaved(next); onWorkspaceSaved?.(next);
      toast('Organisation saved', { tone: 'success' });
    } catch (e) { toast(e.message || 'Save failed', { tone: 'error' }); }
    finally { setSaving(false); }
  };
  const onDiscard = () => { setForm({ ...saved }); toast('Changes discarded'); };

  return (
    <div>
      <Setting label="Organisation name" help="Appears on reports, invoices, and shared links.">
        <input className="input" value={form.org_name} onChange={set('org_name')}/>
      </Setting>
      <Setting label="Workspace URL" help="Your custom Fossyl subdomain.">
        <div className="row ai-c g-0" style={{ border: '1px solid var(--line-2)', borderRadius: 3, overflow: 'hidden' }}>
          <input className="input" value={form.workspace_slug} onChange={set('workspace_slug')} style={{ border: 0, borderRadius: 0 }}/>
          <div style={{ padding: '10px 14px', color: 'var(--muted)', fontFamily: 'var(--mono)', fontSize: 12, background: 'var(--bg-1)', whiteSpace: 'nowrap' }}>.getfossyl.dev</div>
        </div>
      </Setting>
      <Setting label="Industry" help="Used for ingestion heuristics (e.g. hospital billing schema detection).">
        <select className="input" value={form.industry} onChange={set('industry')}>
          <option>Hospital / healthcare</option><option>Research institution</option>
          <option>Government</option><option>Consulting / systems integrator</option><option>Other</option>
        </select>
      </Setting>
      <Setting label="Default codebase framework">
        <SegmentControl options={['Zope 2','Zope 4','Plone 4','Plone 5','Plone 6']} value={form.framework} onChange={fw => setForm(f => ({ ...f, framework: fw }))}/>
      </Setting>
      <SaveBar onSave={onSave} onDiscard={onDiscard} saving={saving} dirty={dirty}/>
    </div>
  );
}

// ── Plan tab ──────────────────────────────────────────────────────────────────
function TabPlan({ go, workspace }) {
  const { paid, plan, role, setRole, setPaid, setPlan } = useAuth();
  const { toast } = useToast();
  const [leaving,       setLeaving]       = useState(false);
  const [confirmLeave,  setConfirmLeave]  = useState(false);

  const ws = workspace || {};
  const wsSlug   = ws.workspace_slug || '—';
  const planLabel = { specimen:'Specimen', field:'Field', team:'Team', agency:'Agency' }[plan] || plan || '—';
  const seats     = { specimen:1, field:2, team:5, agency:null }[plan] || ws.seat_limit || 1;
  const isOwner   = role === 'Owner';

  const handleLeave = async () => {
    setLeaving(true);
    try {
      await api('POST', '/account/workspace/leave');
      // Reset to unpaid solo state
      setPaid(false);
      setPlan('field');
      setRole('Owner');
      localStorage.setItem('fossyl_role', 'Owner');
      setConfirmLeave(false);
      toast('You have left the workspace — choose a plan to start excavating.', { tone: 'success', duration: 4000 });
      go('billing');
    } catch (e) {
      toast(e.message || 'Failed to leave workspace', { tone: 'error' });
    } finally {
      setLeaving(false);
    }
  };

  return (
    <div>
      <div className="panel" style={{ marginBottom: 24 }}>
        <div className="panel-h"><span className="caps">Current plan</span><span className="mono" style={{ fontSize: 11, color: 'var(--muted)' }}>workspace · {wsSlug}</span></div>
        <div className="panel-b" style={{ padding: 24 }}>
          {!paid ? (
            <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
              <span style={{ color: 'var(--bone)', fontFamily: 'var(--mono)', fontSize: 14 }}>◈</span>
              <span style={{ flex: 1, fontSize: 13, color: 'var(--ink-dim)' }}>No active subscription — choose a plan to start excavating.</span>
              <button className="btn btn-primary" onClick={() => go('billing')}>Choose a plan →</button>
            </div>
          ) : (
            <div className="row jc-b ai-s">
              <div>
                <div className="caps" style={{ marginBottom: 8 }}>Tier</div>
                <div className="display" style={{ fontSize: 36, fontWeight: 400, letterSpacing: '-0.02em', lineHeight: 1 }}>{planLabel}</div>
                <div className="mono" style={{ fontSize: 12, color: 'var(--muted)', marginTop: 8 }}>{seats} seats · active plan</div>
              </div>
              <div className="row g-8" style={{ marginTop: 8 }}>
                {isOwner && <button className="btn btn-primary" onClick={() => go('billing')}>Change plan →</button>}
                {isOwner && <button className="btn" onClick={() => go('billing')}>Update payment method</button>}
              </div>
            </div>
          )}
        </div>
      </div>

      {!isOwner && (
        <div className="panel">
          <div className="panel-h"><span className="caps">Workspace membership</span></div>
          <div className="panel-b" style={{ padding: 24 }}>
            <div className="row jc-b ai-c">
              <div>
                <div style={{ fontSize: 13, color: 'var(--ink)' }}>You are a <strong>{role}</strong> in this workspace.</div>
                <div className="mono" style={{ fontSize: 11, color: 'var(--muted)', marginTop: 4 }}>
                  Leaving will remove your access and return you to a solo account.
                </div>
              </div>
              <button
                className="btn"
                style={{ borderColor: 'var(--rust-dim)', color: 'var(--rust)', flexShrink: 0 }}
                onClick={() => setConfirmLeave(true)}
              >
                Leave workspace
              </button>
            </div>
          </div>
        </div>
      )}

      {confirmLeave && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.65)', display: 'grid', placeItems: 'center', zIndex: 100 }}
          onClick={() => setConfirmLeave(false)}>
          <div onClick={e => e.stopPropagation()} style={{ width: 480, background: 'var(--bg)', border: '1px solid var(--rust-dim)', borderRadius: 3, padding: 28 }}>
            <div className="caps" style={{ color: 'var(--rust)', marginBottom: 12 }}>Leave workspace</div>
            <p style={{ fontSize: 13, color: 'var(--ink-dim)', lineHeight: 1.55, margin: '0 0 20px' }}>
              You will lose access to all shared excavations and reports in this workspace. Your account will be reset to an unpaid solo plan — you can subscribe again at any time.
            </p>
            <div className="row jc-e g-8">
              <button className="btn" onClick={() => setConfirmLeave(false)}>Cancel</button>
              <button
                className="btn"
                disabled={leaving}
                style={{ borderColor: 'var(--rust-dim)', color: 'var(--rust)', background: 'rgba(186,90,50,0.08)' }}
                onClick={handleLeave}
              >
                {leaving ? 'Leaving…' : 'Leave workspace'}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Security tab ──────────────────────────────────────────────────────────────
function TabSecurity() {
  const { toast }   = useToast();
  const { signOut } = useAuth();
  const { go }      = useNav();

  // Password change
  const [showPwModal, setShowPwModal] = useState(false);
  const [pwForm,      setPwForm]      = useState({ current: '', newPw: '', confirm: '' });
  const [pwBusy,      setPwBusy]      = useState(false);

  // TOTP / MFA
  const [mfaEnabled,   setMfaEnabled]   = useState(false);
  const [showMfaSetup, setShowMfaSetup] = useState(false);
  const [totpSecret,   setTotpSecret]   = useState(null);
  const [totpCode,     setTotpCode]     = useState('');
  const [mfaBusy,      setMfaBusy]      = useState(false);

  // Current session info (decoded from JWT)
  const [session, setSession] = useState(null);

  const emailUser = isEmailPasswordUser();

  useEffect(() => {
    const id = getToken();
    if (id) {
      try {
        const { iat, email } = JSON.parse(atob(id.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')));
        const ua = navigator.userAgent;
        const browser = /Firefox/i.test(ua) ? 'Firefox' : /Edg/i.test(ua) ? 'Edge' : /Chrome/i.test(ua) ? 'Chrome' : /Safari/i.test(ua) ? 'Safari' : 'Browser';
        setSession({ browser, email, when: new Date(iat * 1000).toLocaleString('en-GB', { dateStyle: 'medium', timeStyle: 'short' }) });
      } catch {}
    }
  }, []);

  const handleChangePassword = async () => {
    if (pwForm.newPw !== pwForm.confirm) { toast('New passwords do not match', { tone: 'error' }); return; }
    if (pwForm.newPw.length < 8)         { toast('Password must be at least 8 characters', { tone: 'error' }); return; }
    setPwBusy(true);
    try {
      await withCognitoSession((user, resolve, reject) => {
        user.changePassword(pwForm.current, pwForm.newPw, (err, result) => {
          if (err) reject(err); else resolve(result);
        });
      });
      setShowPwModal(false);
      setPwForm({ current: '', newPw: '', confirm: '' });
      toast('Password changed', { tone: 'success' });
    } catch (e) { toast(e.message || 'Password change failed', { tone: 'error' }); }
    setPwBusy(false);
  };

  const handleMfaSetup = async () => {
    setMfaBusy(true);
    try {
      const secret = await withCognitoSession((user, resolve, reject) => {
        user.associateSoftwareToken({
          associateSecretCode: resolve,
          onFailure: reject,
        });
      });
      setTotpSecret(secret);
      setShowMfaSetup(true);
    } catch (e) {
      toast(`Authenticator setup failed: ${e.message}`, { tone: 'error', duration: 5000 });
    }
    setMfaBusy(false);
  };

  const handleMfaVerify = async () => {
    if (totpCode.length !== 6) { toast('Enter the 6-digit code from your app', { tone: 'error' }); return; }
    setMfaBusy(true);
    try {
      await withCognitoSession((user, resolve, reject) => {
        user.verifySoftwareToken(totpCode, 'Authenticator app', {
          onSuccess: resolve,
          onFailure: reject,
        });
      });
      await withCognitoSession((user, resolve, reject) => {
        user.setUserMfaPreference(
          null,
          { Enabled: true, PreferredMfa: true },
          (err, r) => err ? reject(err) : resolve(r),
        );
      });
      setMfaEnabled(true);
      setShowMfaSetup(false);
      setTotpSecret(null);
      setTotpCode('');
      toast('Two-factor authentication enabled', { tone: 'success' });
    } catch (e) { toast(e.message, { tone: 'error' }); }
    setMfaBusy(false);
  };

  const handleMfaDisable = async () => {
    setMfaBusy(true);
    try {
      await withCognitoSession((user, resolve, reject) => {
        user.setUserMfaPreference(
          null,
          { Enabled: false, PreferredMfa: false },
          (err, r) => err ? reject(err) : resolve(r),
        );
      });
      setMfaEnabled(false);
      toast('Two-factor authentication disabled');
    } catch (e) { toast(e.message, { tone: 'error' }); }
    setMfaBusy(false);
  };

  const handleSignOutEverywhere = async () => {
    try {
      await withCognitoSession((user, resolve, reject) => {
        user.globalSignOut({ onSuccess: resolve, onFailure: reject });
      });
    } catch {}
    signOut();
    toast('Signed out of all sessions', { tone: 'success' });
    go('login');
  };

  const totpUri = totpSecret && session?.email
    ? `otpauth://totp/Fossyl:${encodeURIComponent(session.email)}?secret=${totpSecret}&issuer=Fossyl`
    : '';
  const qrUrl = totpUri
    ? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(totpUri)}`
    : '';

  return (
    <div>
      <Setting label="Password" help={emailUser ? 'Change your Fossyl account password.' : 'Password is managed by GitHub — sign in with email to set a Fossyl password.'}>
        <button className="btn" disabled={!emailUser} style={{ opacity: emailUser ? 1 : 0.4, cursor: emailUser ? 'pointer' : 'not-allowed' }}
          onClick={() => setShowPwModal(true)}>Change password</button>
      </Setting>

      <Setting label="Two-factor authentication" help={emailUser ? 'TOTP via authenticator app (Google Authenticator, Authy, 1Password).' : 'Not available for GitHub OAuth accounts — 2FA is managed by GitHub.'}>
        <div className="row g-12 ai-c">
          {emailUser ? (
            <>
              <span className={`badge ${mfaEnabled ? 'safe' : ''}`}>
                <span className={`dot ${mfaEnabled ? 'safe' : ''}`}/>{mfaEnabled ? 'enabled' : 'disabled'}
              </span>
              {mfaEnabled
                ? <button className="btn btn-sm" disabled={mfaBusy} onClick={handleMfaDisable}>Disable</button>
                : <button className="btn btn-sm" disabled={mfaBusy} onClick={handleMfaSetup}>{mfaBusy ? 'Loading…' : 'Set up authenticator →'}</button>
              }
            </>
          ) : (
            <span className="badge" style={{ opacity: 0.5 }}>managed by GitHub</span>
          )}
        </div>
      </Setting>

      <Setting label="SSO (SAML)" help="Agency plan only.">
        <button className="btn" style={{ opacity: 0.6 }} onClick={() => toast('SSO is available on Agency plan')}>Upgrade to enable</button>
      </Setting>

      <Setting label="Active sessions" help="Sign out any session you don't recognise.">
        <div className="col g-8">
          {session && (
            <div className="row jc-b ai-c" style={{ padding: '10px 14px', border: '1px solid var(--line)', borderRadius: 2, background: 'var(--bg-1)' }}>
              <div>
                <div style={{ fontSize: 12.5 }}>{session.browser} · {navigator.platform || 'Unknown OS'}</div>
                <div className="mono" style={{ fontSize: 10.5, color: 'var(--muted)' }}>signed in {session.when}</div>
              </div>
              <span className="badge safe">current</span>
            </div>
          )}
        </div>
      </Setting>

      <Setting label="Sign out of all sessions" help="Invalidates every active token on every device immediately.">
        <button className="btn" onClick={handleSignOutEverywhere}>Sign out everywhere</button>
      </Setting>

      {/* Change password modal */}
      {showPwModal && (
        <Modal onClose={() => { setShowPwModal(false); setPwForm({ current: '', newPw: '', confirm: '' }); }}>
          <div className="caps" style={{ marginBottom: 20 }}>Change password</div>
          <div className="col g-12">
            <div>
              <span className="label" style={{ display: 'block', marginBottom: 6 }}>Current password</span>
              <input className="input" type="password" autoFocus value={pwForm.current} onChange={e => setPwForm(f => ({ ...f, current: e.target.value }))}/>
            </div>
            <div>
              <span className="label" style={{ display: 'block', marginBottom: 6 }}>New password</span>
              <input className="input" type="password" value={pwForm.newPw} onChange={e => setPwForm(f => ({ ...f, newPw: e.target.value }))}/>
            </div>
            <div>
              <span className="label" style={{ display: 'block', marginBottom: 6 }}>Confirm new password</span>
              <input className="input" type="password" value={pwForm.confirm} onChange={e => setPwForm(f => ({ ...f, confirm: e.target.value }))}
                onKeyDown={e => e.key === 'Enter' && !pwBusy && handleChangePassword()}/>
            </div>
            <div className="row jc-e g-8" style={{ marginTop: 8 }}>
              <button className="btn" onClick={() => setShowPwModal(false)}>Cancel</button>
              <button className="btn btn-primary" disabled={pwBusy || !pwForm.current || !pwForm.newPw || !pwForm.confirm} onClick={handleChangePassword}>
                {pwBusy ? 'Changing…' : 'Change password →'}
              </button>
            </div>
          </div>
        </Modal>
      )}

      {/* TOTP setup modal */}
      {showMfaSetup && totpSecret && (
        <Modal onClose={() => { setShowMfaSetup(false); setTotpSecret(null); setTotpCode(''); }} width={480}>
          <div className="caps" style={{ marginBottom: 16 }}>Set up authenticator</div>
          <div className="col g-16">
            <p style={{ fontSize: 13, color: 'var(--ink-dim)', margin: 0, lineHeight: 1.55 }}>
              Scan the QR code with your authenticator app, then enter the 6-digit code to confirm.
            </p>
            <div style={{ display: 'flex', justifyContent: 'center', padding: '8px 0' }}>
              <img src={qrUrl} alt="TOTP QR code" width={200} height={200} style={{ imageRendering: 'pixelated' }}/>
            </div>
            <div>
              <div className="caps" style={{ fontSize: 10, marginBottom: 6 }}>Or enter the secret key manually</div>
              <div className="mono" style={{ fontSize: 11.5, padding: '8px 12px', background: 'var(--bg-1)', border: '1px solid var(--line)', borderRadius: 2, letterSpacing: '0.08em', wordBreak: 'break-all' }}>
                {totpSecret}
              </div>
            </div>
            <div>
              <span className="label" style={{ display: 'block', marginBottom: 6 }}>Verification code from your app</span>
              <input className="input mono" maxLength={6} value={totpCode} placeholder="000000"
                style={{ letterSpacing: '0.3em', fontSize: 18, textAlign: 'center' }}
                onChange={e => setTotpCode(e.target.value.replace(/\D/g, ''))}
                onKeyDown={e => e.key === 'Enter' && totpCode.length === 6 && !mfaBusy && handleMfaVerify()}/>
            </div>
            <div className="row jc-e g-8">
              <button className="btn" onClick={() => { setShowMfaSetup(false); setTotpSecret(null); setTotpCode(''); }}>Cancel</button>
              <button className="btn btn-primary" disabled={mfaBusy || totpCode.length !== 6} onClick={handleMfaVerify}>
                {mfaBusy ? 'Verifying…' : 'Verify & enable →'}
              </button>
            </div>
          </div>
        </Modal>
      )}
    </div>
  );
}

// ── Data handling tab ─────────────────────────────────────────────────────────
function TabData({ profile }) {
  const { toast }       = useToast();
  const { paid, plan }  = useAuth();
  const [exporting,  setExporting]  = useState(false);
  const [lastExport, setLastExport] = useState(profile?.last_export_at || '');

  const PLAN_RETENTION = { specimen: '30 days', field: '90 days', team: '1 year', agency: 'Until deleted' };
  const retentionLabel = paid ? (PLAN_RETENTION[plan] || plan || '—') : null;

  const handleExport = async () => {
    setExporting(true);
    try {
      const res = await api('POST', '/account/export');
      setLastExport(res.exported_at || '');
      toast('Export sent — check your email inbox.', { tone: 'success', duration: 4000 });
    } catch (e) {
      toast(e.message || 'Export failed', { tone: 'error' });
    }
    setExporting(false);
  };

  return (
    <div>
      <Setting label="Report retention" help="Determined by your active plan. Source uploads are always deleted immediately after scan.">
        {paid ? (
          <div className="row g-12 ai-c">
            <span className="badge safe">{retentionLabel}</span>
            <span className="mono" style={{ fontSize: 11, color: 'var(--muted)' }}>set by your {plan} plan · cannot be shortened</span>
          </div>
        ) : (
          <span className="mono" style={{ fontSize: 12, color: 'var(--muted)' }}>No active subscription — retention follows your plan after subscribing.</span>
        )}
      </Setting>
      <Setting label="Export everything" help="Sends your profile, workspace, and report metadata to your account email as JSON.">
        <div className="row g-12 ai-c">
          <button className="btn" disabled={exporting} onClick={handleExport}>
            {exporting ? 'Exporting…' : 'Export data'}
          </button>
          {lastExport && (
            <span className="mono" style={{ fontSize: 11, color: 'var(--muted)' }}>last export: {lastExport.slice(0, 10)}</span>
          )}
        </div>
      </Setting>
    </div>
  );
}

// ── API tokens tab ────────────────────────────────────────────────────────────
function TabApi() {
  const { toast } = useToast();
  const [tokens,   setTokens]  = useState([]);
  const [loading,  setLoading] = useState(true);
  const [newToken, setNewToken] = useState(null);
  const [busy,     setBusy]    = useState(false);

  useEffect(() => {
    api('GET', '/account/tokens')
      .then(d => { setTokens(d.tokens || []); setLoading(false); })
      .catch(() => { setTokens([]); setLoading(false); });
  }, []);

  const revoke = async prefix => {
    setBusy(true);
    try { await api('DELETE', `/account/tokens/${prefix}`); setTokens(ts => ts.filter(t => t.prefix !== prefix)); toast('Token revoked', { tone: 'success' }); }
    catch (e) { toast(e.message, { tone: 'error' }); }
    finally { setBusy(false); }
  };

  const generate = async () => {
    setBusy(true);
    try {
      const res = await api('POST', '/account/tokens', { label: 'New token' });
      setTokens(ts => [...ts, { prefix: res.prefix, label: res.label, created_at: res.created_at, last_used: 'never' }]);
      setNewToken(res.token);
      toast('Copy the token now — it won\'t be shown again', { tone: 'success', duration: 4000 });
    } catch (e) { toast(e.message, { tone: 'error' }); }
    finally { setBusy(false); }
  };

  const copy = () => { navigator.clipboard?.writeText(newToken).catch(() => {}); toast('Copied', { tone: 'success' }); setNewToken(null); };

  return (
    <div>
      <Setting label="Personal API tokens" help="Use with the Fossyl CLI to upload scans from your local machine or CI.">
        <div className="col g-8">
          {loading && <div className="mono" style={{ fontSize: 12, color: 'var(--dim)' }}>loading tokens…</div>}
          {newToken && (
            <div className="row jc-b ai-c" style={{ padding: '10px 14px', border: '1px solid var(--lichen-dim)', borderRadius: 2, background: 'rgba(70,120,80,0.08)' }}>
              <div><div className="mono" style={{ fontSize: 10.5, color: 'var(--lichen)', marginBottom: 4 }}>New token — copy now, shown once only</div><div className="mono" style={{ fontSize: 12, color: 'var(--ink)', letterSpacing: '0.02em' }}>{newToken}</div></div>
              <button className="btn btn-sm" onClick={copy} style={{ flexShrink: 0, marginLeft: 16 }}>Copy →</button>
            </div>
          )}
          {tokens.map(t => (
            <div key={t.prefix} className="row jc-b ai-c" style={{ padding: '10px 14px', border: '1px solid var(--line)', borderRadius: 2, background: 'var(--bg-1)' }}>
              <div><div className="mono" style={{ fontSize: 12, color: 'var(--ink)' }}>{t.prefix}••••</div><div className="mono" style={{ fontSize: 10.5, color: 'var(--muted)' }}>{t.label} · created {t.created_at?.slice(0,10)} · last used {t.last_used}</div></div>
              <button className="btn btn-sm" disabled={busy} onClick={() => revoke(t.prefix)}>Revoke</button>
            </div>
          ))}
          <button className="btn btn-primary btn-sm" style={{ width: 'fit-content' }} disabled={busy} onClick={generate}>Generate new token</button>
        </div>
      </Setting>
      <Setting label="CLI quick start">
        <pre className="mono" style={{ margin: 0, padding: 16, background: 'var(--bg-1)', border: '1px solid var(--line)', fontSize: 12, color: 'var(--ink-dim)', lineHeight: 1.7, borderRadius: 2 }}>{`$ npm i -g @fossyl/cli\n$ fossyl auth pat_live_F0a3f...\n$ fossyl scan ./plone-site.zip --zodb ./Data.fs.gz\n  → scan queued · F-8a41\n  → report ready in 48s\n  → https://charite.getfossyl.dev/r/F-8a41`}</pre>
      </Setting>
    </div>
  );
}

// ── Notifications tab ─────────────────────────────────────────────────────────
const NOTIF_DEFAULTS = {
  scanEmail: true,  scanApp: true,
  riskEmail: true,  riskApp: true,
  digestEmail: true,digestApp: false,
  teamEmail: false, teamApp: false,
  billingEmail: true,billingApp: false,
};

function TabNotifications() {
  const { toast }           = useToast();
  const [prefs, setPrefs]   = useState(() => storageGet('fossyl_notifs', NOTIF_DEFAULTS));
  const [loading, setLoading] = useState(true);
  const [saving,  setSaving]  = useState(false);

  useEffect(() => {
    api('GET', '/account/notifications')
      .then(d => { const p = { ...NOTIF_DEFAULTS, ...(d.prefs || {}) }; setPrefs(p); storageSet('fossyl_notifs', p); })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, []);

  const set = k => e => {
    const next = { ...prefs, [k]: e.target.checked };
    setPrefs(next);
    storageSet('fossyl_notifs', next);
    setSaving(true);
    api('PUT', '/account/notifications', { prefs: next })
      .catch(() => toast('Could not save preferences', { tone: 'error' }))
      .finally(() => setSaving(false));
  };

  const Check = ({ k, label }) => (
    <label className="row g-8 ai-c" style={{ cursor: 'pointer', minWidth: 80 }}>
      <input type="checkbox" checked={!!prefs[k]} onChange={set(k)} style={{ accentColor: 'var(--bone)' }}/>
      <span style={{ fontSize: 13 }}>{label}</span>
    </label>
  );

  const Row = ({ prefix }) => (
    <div className="row g-24">
      <Check k={`${prefix}Email`} label="Email"/>
      <Check k={`${prefix}App`}   label="In-app"/>
    </div>
  );

  if (loading) return <div className="mono" style={{ fontSize: 12, color: 'var(--dim)', padding: '24px 0' }}>loading preferences…</div>;

  return (
    <div>
      {saving && <div className="mono" style={{ fontSize: 10.5, color: 'var(--muted)', marginBottom: 16 }}>saving…</div>}
      <Setting label="Scan completed"             help="Email from noreply@getfossyl.dev · in-app toast in the bottom-right corner."><Row prefix="scan"/></Setting>
      <Setting label="High-risk artifact detected" help="Artifact with risk ≥ 90 and zero version control."><Row prefix="risk"/></Setting>
      <Setting label="Weekly divergence digest"    help="Summary of new TTW artifacts and coupling drift. Sent every Monday."><Row prefix="digest"/></Setting>
      <Setting label="Team activity"               help="Teammate added a comment or marked an artifact safe-to-delete."><Row prefix="team"/></Setting>
      <Setting label="Billing events"              help="Invoice paid, card declined, plan changed."><Row prefix="billing"/></Setting>
    </div>
  );
}

// ── Danger zone ───────────────────────────────────────────────────────────────
function TabDanger({ go, members }) {
  const { signOut, role } = useAuth();
  const { toast }         = useToast();

  const [confirmDelete,  setConfirmDelete]  = useState(false);
  const [confirmText,    setConfirmText]    = useState('');
  const [showTransfer,   setShowTransfer]   = useState(false);
  const [transferEmail,  setTransferEmail]  = useState('');
  const [transferBusy,   setTransferBusy]   = useState(false);
  const [disbandConfirm, setDisbandConfirm] = useState(false);

  const wsSlug      = (_sidebarWorkspace || {}).workspace_slug || 'workspace';
  const deletePhrase = `DELETE ${wsSlug}`;
  const isOwner     = role === 'Owner';
  const targets     = (members || []).filter(m => m.status === 'active' && m.role !== 'Owner');

  const handleSignOut = () => {
    const user = _getCognitoSDKUser?.();
    if (user) { try { user.signOut(); } catch {} }
    signOut();
    toast('Signed out', { tone: 'success' });
    go('landing');
  };

  const handleSignOutEverywhere = async () => {
    try {
      await withCognitoSession((user, resolve, reject) => {
        user.globalSignOut({ onSuccess: resolve, onFailure: reject });
      });
    } catch {}
    const user = _getCognitoSDKUser?.();
    if (user) { try { user.signOut(); } catch {} }
    signOut();
    toast('Signed out of all sessions', { tone: 'success' });
    go('login');
  };

  const handleTransfer = async () => {
    if (!transferEmail) { toast('Select a member first', { tone: 'error' }); return; }
    setTransferBusy(true);
    try {
      await api('POST', '/account/workspace/transfer', { target_email: transferEmail });
      toast(`Transfer request sent to ${transferEmail}`, { tone: 'success', duration: 4000 });
      setShowTransfer(false);
      setTransferEmail('');
    } catch (e) { toast(e.message || 'Transfer failed', { tone: 'error' }); }
    setTransferBusy(false);
  };

  const handleDisband = async () => {
    setTransferBusy(true);
    try {
      await api('POST', '/account/workspace/disband');
      toast('Team disbanded — all members removed and notified.', { tone: 'success', duration: 5000 });
      setShowTransfer(false);
      setDisbandConfirm(false);
    } catch (e) { toast(e.message || 'Disband failed', { tone: 'error' }); }
    setTransferBusy(false);
  };

  return (
    <div>
      <div style={{ padding: '20px 24px', border: '1px solid var(--rust-dim)', borderRadius: 3, background: 'rgba(186,90,50,0.05)', marginBottom: 24 }}>
        <div className="caps" style={{ color: 'var(--rust)', marginBottom: 8 }}>⚠ Irreversible actions</div>
        <p style={{ fontSize: 13, color: 'var(--ink-dim)', margin: 0, lineHeight: 1.55, maxWidth: 640 }}>Sign-out is safe. Account deletion permanently destroys all reports, audit logs, and team data within seven days.</p>
      </div>

      <Setting label="Sign out of this session" help="Clears this browser's session — you will need to sign in again on this device.">
        <button className="btn" onClick={handleSignOut}>Sign out</button>
      </Setting>

      <Setting label="Sign out of all sessions" help="Invalidates every active token on every device and browser immediately.">
        <button className="btn" onClick={handleSignOutEverywhere}>Sign out everywhere</button>
      </Setting>

      {isOwner && (
        <Setting label="Transfer ownership" help="Hand the workspace to another active member, or disband the team entirely.">
          <button className="btn" onClick={() => { setShowTransfer(true); setDisbandConfirm(false); setTransferEmail(''); }}>Transfer ownership…</button>
        </Setting>
      )}

      <Setting label="Delete account" help="Permanent. All data purged within 7 days.">
        <button className="btn" style={{ borderColor: 'var(--rust-dim)', color: 'var(--rust)' }} onClick={() => setConfirmDelete(true)}>Delete account…</button>
      </Setting>

      {/* Transfer ownership modal */}
      {showTransfer && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.65)', display: 'grid', placeItems: 'center', zIndex: 100 }}
          onClick={() => { setShowTransfer(false); setDisbandConfirm(false); }}>
          <div onClick={e => e.stopPropagation()} style={{ width: 520, background: 'var(--bg)', border: '1px solid var(--line-2)', borderRadius: 3, padding: 28 }}>
            {!disbandConfirm ? (
              <>
                <div className="caps" style={{ marginBottom: 16 }}>Transfer ownership</div>
                <p style={{ fontSize: 13, color: 'var(--ink-dim)', margin: '0 0 20px', lineHeight: 1.55 }}>
                  The selected member will receive an in-app request. Once they accept, you become a Member.
                </p>
                {targets.length > 0 ? (
                  <div className="col g-6" style={{ marginBottom: 20 }}>
                    {targets.map(m => (
                      <label key={m.email} className="row g-12 ai-c" style={{ padding: '10px 14px', border: `1px solid ${transferEmail === m.email ? 'var(--bone)' : 'var(--line)'}`, borderRadius: 2, cursor: 'pointer', background: transferEmail === m.email ? 'var(--bg-2)' : 'transparent' }}>
                        <input type="radio" name="xfer" value={m.email} checked={transferEmail === m.email} onChange={() => setTransferEmail(m.email)} style={{ accentColor: 'var(--bone)', flexShrink: 0 }}/>
                        <div>
                          <div style={{ fontSize: 13 }}>{m.display_name || m.email.split('@')[0]}</div>
                          <div className="mono" style={{ fontSize: 10.5, color: 'var(--muted)' }}>{m.email} · {m.role}</div>
                        </div>
                      </label>
                    ))}
                  </div>
                ) : (
                  <div className="mono" style={{ fontSize: 12, color: 'var(--muted)', marginBottom: 20 }}>No other active members — invite members first.</div>
                )}
                <div className="row jc-b ai-c" style={{ paddingTop: 16, borderTop: '1px solid var(--line)' }}>
                  <button className="btn btn-sm" style={{ borderColor: 'var(--rust-dim)', color: 'var(--rust)' }}
                    onClick={() => setDisbandConfirm(true)}>Delete team →</button>
                  <div className="row g-8">
                    <button className="btn" onClick={() => setShowTransfer(false)}>Cancel</button>
                    <button className="btn btn-primary" disabled={!transferEmail || transferBusy} onClick={handleTransfer}>
                      {transferBusy ? 'Sending…' : 'Send transfer request →'}
                    </button>
                  </div>
                </div>
              </>
            ) : (
              <>
                <div className="caps" style={{ color: 'var(--rust)', marginBottom: 14 }}>Delete team</div>
                <p style={{ fontSize: 13, color: 'var(--ink-dim)', lineHeight: 1.55, margin: '0 0 20px' }}>
                  This will remove all <strong>{targets.length}</strong> member{targets.length !== 1 ? 's' : ''} from the workspace. Each will be notified by email and moved to a solo account. You remain as the sole owner of your workspace.
                </p>
                <div className="row jc-e g-8">
                  <button className="btn" onClick={() => setDisbandConfirm(false)}>Go back</button>
                  <button className="btn" disabled={transferBusy}
                    style={{ borderColor: 'var(--rust)', color: 'var(--rust)', background: 'rgba(186,90,50,0.08)' }}
                    onClick={handleDisband}>
                    {transferBusy ? 'Disbanding…' : 'Disband team'}
                  </button>
                </div>
              </>
            )}
          </div>
        </div>
      )}

      {/* Delete account modal */}
      {confirmDelete && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.65)', display: 'grid', placeItems: 'center', zIndex: 100 }} onClick={() => { setConfirmDelete(false); setConfirmText(''); }}>
          <div onClick={e => e.stopPropagation()} style={{ width: 520, background: 'var(--bg)', border: '1px solid var(--rust-dim)', borderRadius: 3, padding: 28 }}>
            <div className="caps" style={{ color: 'var(--rust)' }}>Confirm — delete account</div>
            <h3 className="serif" style={{ fontSize: 26, fontWeight: 400, margin: '14px 0 12px' }}>This will erase everything.</h3>
            <p style={{ fontSize: 13.5, color: 'var(--ink-dim)', lineHeight: 1.55, margin: '0 0 20px' }}>You are about to permanently delete the <span className="bone">{wsSlug}</span> workspace and all associated reports, team members, API tokens, and audit logs. There is no undo.</p>
            <div style={{ marginBottom: 16 }}>
              <label className="caps" style={{ display: 'block', marginBottom: 8 }}>Type <span className="mono" style={{ color: 'var(--rust)' }}>{deletePhrase}</span> to confirm</label>
              <input className="input mono" value={confirmText} onChange={e => setConfirmText(e.target.value)} placeholder={deletePhrase} autoFocus/>
            </div>
            <div className="row jc-e g-8">
              <button className="btn" onClick={() => { setConfirmDelete(false); setConfirmText(''); }}>Cancel</button>
              <button className="btn" disabled={confirmText !== deletePhrase}
                style={{ borderColor: confirmText===deletePhrase?'var(--rust)':'var(--rust-dim)', color: confirmText===deletePhrase?'var(--rust)':'var(--muted)', background: confirmText===deletePhrase?'rgba(186,90,50,0.08)':'transparent', cursor: confirmText===deletePhrase?'pointer':'not-allowed' }}
                onClick={() => { setConfirmDelete(false); setConfirmText(''); toast('Account scheduled for deletion.', { tone: 'error', duration: 4000 }); setTimeout(() => { signOut(); go('landing'); }, 600); }}>
                Delete permanently
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Sidebar + top bar ─────────────────────────────────────────────────────────
let _sidebarProfile   = null;
let _sidebarWorkspace = null;
let _sbMembers        = null;

function AccountSidebar({ current, go }) {
  // Read from module cache (set by AccountPage on load) or fall back to blank
  const p  = _sidebarProfile   || {};
  const ws = _sidebarWorkspace  || {};

  const name      = p.display_name || '';
  const wsSlug    = ws.workspace_slug || '';
  const wsPlan    = ws.plan       || 'team';
  const wsSeats   = ws.seat_limit || 5;
  const activeSeats = (_sbMembers || []).filter(m => m.status === 'active').length;
  const av        = initials(name || p.email || '?');
  const short     = name
    ? name.split(' ').filter(Boolean).map((w,i) => i===0 ? w[0]+'.' : w).join(' ')
    : '—';

  const planLabel = { specimen:'Specimen', field:'Field', team:'Team', agency:'Agency' }[wsPlan] || wsPlan;

  const items = [
    { id:'upload',  num:'§X', label:'New excavation' },
    { id:'reports', num:'§R', label:'Report'         },
    { id:'account', num:'§A', label:'Account'        },
    { id:'billing', num:'§B', label:'Billing'        },
    { id:'landing', num:'↩',  label:'Back to landing'},
  ];

  return (
    <aside style={{ borderRight:'1px solid var(--line)', background:'var(--bg-1)', padding:'18px 0', display:'flex', flexDirection:'column', gap:16 }}>
      <div style={{ padding:'0 20px 16px', borderBottom:'1px solid var(--line)' }}>
        <a onClick={() => go('landing')} style={{ cursor: 'pointer' }}><Logo size={20}/></a>
      </div>

      <div style={{ padding:'0 20px' }}>
        <span className="caps" style={{ display:'block', marginBottom:10 }}>Workspace</span>
        <div style={{ border:'1px solid var(--line)', padding:'10px 12px', borderRadius:2, background:'var(--bg)' }}>
          <div className="mono" style={{ fontSize:10.5, color:'var(--dim)' }}>workspace_id</div>
          <div className="serif" style={{ fontSize:15, marginTop:2 }}>
            {wsSlug ? `${wsSlug}.getfossyl.dev` : '—'}
          </div>
          <div className="mono" style={{ fontSize:10.5, color:'var(--muted)', marginTop:4 }}>
            {planLabel} · {activeSeats}/{wsSeats} seats
          </div>
        </div>
      </div>

      <nav style={{ padding:'0 8px', flex:1 }}>
        {items.map(s => (
          <button key={s.id} onClick={() => go(s.id)} style={{
            background: current===s.id?'var(--bg-2)':'transparent', border:0, textAlign:'left',
            padding:'8px 12px', color: current===s.id?'var(--ink)':'var(--ink-dim)',
            fontFamily:'var(--sans)', fontSize:13, display:'flex', gap:10, alignItems:'center',
            cursor:'pointer', width:'100%',
            borderLeft: current===s.id?'1px solid var(--bone)':'1px solid transparent',
          }}>
            <span className="mono" style={{ color:'var(--dim)', fontSize:11, minWidth:20 }}>{s.num}</span>
            {s.label}
          </button>
        ))}
      </nav>

      <button onClick={() => go('account')} style={{ padding:'14px 20px', background:'transparent', border:0, borderTop:'1px solid var(--line)', textAlign:'left', cursor:'pointer', width:'100%', display:'flex', gap:10, alignItems:'center' }}>
        <div style={{ width:28, height:28, borderRadius:'50%', background:'var(--bg-2)', border:'1px solid var(--line-2)', display:'grid', placeItems:'center', fontFamily:'var(--serif)', fontSize:13, color:'var(--bone)', flexShrink:0 }}>{av}</div>
        <div className="col" style={{ flex:1, minWidth:0 }}>
          <span style={{ fontSize:12, fontWeight:500, color:'var(--ink)', display:'block', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{short || '—'}</span>
          <span className="mono" style={{ fontSize:10.5, color:'var(--muted)' }}>Owner · settings →</span>
        </div>
      </button>
    </aside>
  );
}

function AccountTopBar({ crumb }) {
  const { go }      = useNav();
  const { signOut } = useAuth();
  const { toast }   = useToast();
  const ws = _sidebarWorkspace || {};
  const wsSlug = ws.workspace_slug || '';
  return (
    <div style={{ borderBottom:'1px solid var(--line)', padding:'12px 32px', background:'var(--bg)', display:'flex', alignItems:'center', justifyContent:'space-between', position:'sticky', top:0, zIndex:10 }}>
      <div className="row g-8 ai-c" style={{ fontSize:12, color:'var(--muted)', fontFamily:'var(--mono)' }}>
        {crumb.map((c,i) => (
          <React.Fragment key={i}>
            {i>0 && <span className="dim">/</span>}
            <span className={i===crumb.length-1?'ink':''}>{c}</span>
          </React.Fragment>
        ))}
      </div>
      <div className="row g-12 ai-c">
        {wsSlug && <span className="mono" style={{ fontSize:11, color:'var(--muted)' }}>workspace · {wsSlug}</span>}
        {wsSlug && <span className="dim">·</span>}
        <a className="mono" style={{ fontSize:11, color:'var(--ink-dim)', cursor:'pointer' }} onClick={() => go('account')}>Account</a>
        <a className="mono" style={{ fontSize:11, color:'var(--ink-dim)', cursor:'pointer' }} onClick={() => { signOut(); toast('Signed out', { tone:'success' }); go('landing'); }}>Sign out</a>
      </div>
    </div>
  );
}

Object.assign(window, { AccountPage, AccountSidebar, AccountTopBar });