// SETTINGS PLATFORM — shared infrastructure
// Implements cross-cutting concerns: search, toast, dirty-tracker, tooltip,
// empty state, skeleton, danger zone, typed-confirm, audit side-panel,
// impact preview, compliance breakdown, hash router, settings-as-code export.
//
// All exposed via window.* for use across view files.

const { useState: useInfraState, useEffect: useInfraEffect, useRef: useInfraRef, useCallback: useInfraCb, useMemo: useInfraMemo, createContext: createInfraCtx, useContext: useInfraCtx } = React;
const Tin = window.ArbiterTokens;

// ────────────────────────────────────────────────────────────────────────────
// 1. TOAST SYSTEM (with undo) — #21
// ────────────────────────────────────────────────────────────────────────────
const ToastCtx = createInfraCtx({ push: () => {} });

function ToastProvider({ children }) {
  const [toasts, setToasts] = useInfraState([]);
  const push = useInfraCb((t) => {
    const id = Math.random().toString(36).slice(2);
    const toast = { id, kind: 'info', ttl: 5000, ...t };
    setToasts(ts => [...ts, toast]);
    if (toast.ttl > 0) {
      setTimeout(() => setToasts(ts => ts.filter(x => x.id !== id)), toast.ttl);
    }
    return id;
  }, []);
  const dismiss = useInfraCb((id) => setToasts(ts => ts.filter(x => x.id !== id)), []);

  const kindColor = (k) => ({
    success: { bg: '#059669', ring: 'rgba(5,150,105,0.3)' },
    error:   { bg: '#B91C1C', ring: 'rgba(185,28,28,0.3)' },
    warn:    { bg: '#D97706', ring: 'rgba(217,119,6,0.3)' },
    info:    { bg: '#334155', ring: 'rgba(51,65,85,0.3)' },
  }[k] || { bg: '#334155', ring: 'rgba(51,65,85,0.3)' });

  return (
    <ToastCtx.Provider value={{ push, dismiss }}>
      {children}
      <div style={{
        position: 'fixed', bottom: '24px', right: '24px', zIndex: 9999,
        display: 'flex', flexDirection: 'column', gap: '8px', maxWidth: '420px',
      }}>
        {toasts.map(t => {
          const c = kindColor(t.kind);
          return (
            <div key={t.id} style={{
              background: '#fff', color: Tin.color.text.primary,
              border: `1px solid ${Tin.color.border.light}`,
              borderLeft: `4px solid ${c.bg}`,
              borderRadius: '8px', boxShadow: `0 4px 12px ${c.ring}`,
              padding: '10px 14px', fontSize: '12px',
              display: 'flex', alignItems: 'flex-start', gap: '10px', minWidth: '280px',
            }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontWeight: 700, marginBottom: t.message ? '2px' : 0 }}>{t.title}</div>
                {t.message && <div style={{ color: Tin.color.text.tertiary, fontSize: '11px', lineHeight: 1.45 }}>{t.message}</div>}
              </div>
              {t.onUndo && (
                <button onClick={() => { t.onUndo(); dismiss(t.id); }} style={{
                  background: 'transparent', border: `1px solid ${Tin.color.border.medium}`,
                  color: '#334155', fontWeight: 700, fontSize: '10px', padding: '4px 10px',
                  borderRadius: '5px', cursor: 'pointer', textTransform: 'uppercase', letterSpacing: '0.04em',
                }}>Undo</button>
              )}
              <button onClick={() => dismiss(t.id)} style={{
                background: 'none', border: 'none', color: Tin.color.text.tertiary,
                cursor: 'pointer', fontSize: '14px', lineHeight: 1, padding: 0,
              }}>×</button>
            </div>
          );
        })}
      </div>
    </ToastCtx.Provider>
  );
}

function useToast() { return useInfraCtx(ToastCtx); }

// ────────────────────────────────────────────────────────────────────────────
// 2. DIRTY TRACKER — unsaved changes guard with sticky footer — #19
// ────────────────────────────────────────────────────────────────────────────
const DirtyCtx = createInfraCtx({ mark: () => {}, clear: () => {}, dirty: false });

function DirtyProvider({ children }) {
  const [dirtyKeys, setDirtyKeys] = useInfraState([]);
  const [savingKey, setSavingKey] = useInfraState(null);
  const mark = useInfraCb((key) => setDirtyKeys(k => Array.from(new Set([...k, key]))), []);
  const clear = useInfraCb((key) => setDirtyKeys(k => k.filter(x => x !== key)), []);
  const clearAll = useInfraCb(() => setDirtyKeys([]), []);

  useInfraEffect(() => {
    const h = (e) => {
      if (dirtyKeys.length > 0) {
        e.preventDefault();
        e.returnValue = '';
      }
    };
    window.addEventListener('beforeunload', h);
    return () => window.removeEventListener('beforeunload', h);
  }, [dirtyKeys]);

  return (
    <DirtyCtx.Provider value={{ mark, clear, clearAll, dirty: dirtyKeys.length > 0, dirtyKeys, savingKey, setSavingKey }}>
      {children}
    </DirtyCtx.Provider>
  );
}

function useDirty() { return useInfraCtx(DirtyCtx); }

function DirtyFooter() {
  const { dirty, dirtyKeys, clearAll } = useDirty();
  const toast = useToast();
  if (!dirty) return null;
  return (
    <div style={{
      position: 'sticky', bottom: 0, left: 0, right: 0, zIndex: 100,
      background: '#fffbeb', borderTop: '2px solid #D97706',
      padding: '12px 24px', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      gap: '12px', boxShadow: '0 -4px 16px rgba(217,119,6,0.12)',
    }}>
      <div style={{ fontSize: '12px', color: '#78350F', fontWeight: 600 }}>
        ● You have {dirtyKeys.length} unsaved change{dirtyKeys.length === 1 ? '' : 's'} · {dirtyKeys.join(' · ')}
      </div>
      <div style={{ display: 'flex', gap: '8px' }}>
        <button onClick={() => { clearAll(); toast.push({ kind: 'info', title: 'Changes discarded' }); }} style={{
          padding: '7px 14px', borderRadius: '6px', border: '1px solid #D97706',
          background: '#fff', color: '#D97706', fontWeight: 600, fontSize: '12px', cursor: 'pointer',
        }}>Discard</button>
        <button onClick={() => {
          const n = dirtyKeys.length;
          clearAll();
          toast.push({ kind: 'success', title: `Saved ${n} change${n === 1 ? '' : 's'}`, onUndo: () => toast.push({ kind: 'info', title: 'Restored previous values' }) });
        }} style={{
          padding: '7px 14px', borderRadius: '6px', background: '#D97706', border: 'none',
          color: '#fff', fontWeight: 700, fontSize: '12px', cursor: 'pointer',
        }}>Save all</button>
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// 3. TOOLTIP — #26
// ────────────────────────────────────────────────────────────────────────────
function InfoTip({ text, recommended }) {
  const [open, setOpen] = useInfraState(false);
  return (
    <span style={{ position: 'relative', display: 'inline-flex', marginLeft: '6px', verticalAlign: 'middle' }}>
      <span
        onMouseEnter={() => setOpen(true)}
        onMouseLeave={() => setOpen(false)}
        onFocus={() => setOpen(true)}
        onBlur={() => setOpen(false)}
        tabIndex={0}
        style={{
          width: '14px', height: '14px', borderRadius: '50%', fontSize: '9px',
          background: Tin.color.bg.secondary, color: Tin.color.text.tertiary,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          cursor: 'help', fontWeight: 700, border: `1px solid ${Tin.color.border.medium}`,
        }}>?</span>
      {open && (
        <span style={{
          position: 'absolute', top: 'calc(100% + 6px)', left: '-8px', zIndex: 50,
          background: '#1E293B', color: '#fff', fontSize: '11px', lineHeight: 1.5,
          padding: '8px 12px', borderRadius: '6px', width: '240px',
          boxShadow: '0 6px 16px rgba(0,0,0,0.18)', fontWeight: 400,
        }}>
          {recommended && (
            <span style={{
              display: 'inline-block', marginBottom: '4px',
              fontSize: '9px', fontWeight: 700, padding: '1px 6px', borderRadius: '3px',
              background: 'rgba(5,150,105,0.2)', color: '#34D399', letterSpacing: '0.06em', textTransform: 'uppercase',
            }}>Recommended</span>
          )}
          <div>{text}</div>
        </span>
      )}
    </span>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// 4. EMPTY STATE — #22
// ────────────────────────────────────────────────────────────────────────────
function EmptyState({ icon = '∅', title, hint, action }) {
  return (
    <div style={{
      padding: '40px 20px', textAlign: 'center',
      border: `1px dashed ${Tin.color.border.medium}`, borderRadius: '8px',
      background: Tin.color.bg.secondary,
    }}>
      <div style={{ fontSize: '28px', color: Tin.color.text.tertiary, marginBottom: '6px' }}>{icon}</div>
      <div style={{ fontSize: '13px', fontWeight: 700, color: Tin.color.text.primary, marginBottom: '4px' }}>{title}</div>
      {hint && <div style={{ fontSize: '11px', color: Tin.color.text.tertiary, maxWidth: '380px', margin: '0 auto 10px', lineHeight: 1.5 }}>{hint}</div>}
      {action}
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// 5. SKELETON LOADER — #29
// ────────────────────────────────────────────────────────────────────────────
function Skeleton({ width = '100%', height = '12px', radius = '4px', style }) {
  return (
    <div style={{
      width, height, borderRadius: radius,
      background: 'linear-gradient(90deg, rgba(100,116,139,0.08) 0%, rgba(100,116,139,0.18) 50%, rgba(100,116,139,0.08) 100%)',
      backgroundSize: '200% 100%',
      animation: 'stg-shimmer 1.4s ease-in-out infinite',
      ...style,
    }} />
  );
}

// Inject shimmer keyframes once
if (typeof document !== 'undefined' && !document.getElementById('stg-shimmer-kf')) {
  const s = document.createElement('style');
  s.id = 'stg-shimmer-kf';
  s.textContent = '@keyframes stg-shimmer { 0% { background-position: 200% 0 } 100% { background-position: -200% 0 } }';
  document.head.appendChild(s);
}

// ────────────────────────────────────────────────────────────────────────────
// 6. DANGER ZONE + TYPED-CONFIRM — #24
// ────────────────────────────────────────────────────────────────────────────
function DangerZone({ title, children }) {
  return (
    <div style={{
      border: '1px solid rgba(185,28,28,0.35)',
      borderRadius: '8px', background: 'rgba(185,28,28,0.03)',
      marginTop: '20px',
    }}>
      <div style={{
        padding: '10px 16px', borderBottom: '1px solid rgba(185,28,28,0.18)',
        fontSize: '11px', fontWeight: 700, color: '#B91C1C',
        textTransform: 'uppercase', letterSpacing: '0.08em',
      }}>
        ! {title || 'Danger Zone'}
      </div>
      <div>{children}</div>
    </div>
  );
}

function TypedConfirmDialog({ open, title, message, requireText, onConfirm, onCancel, destructive = true }) {
  const [typed, setTyped] = useInfraState('');
  useInfraEffect(() => { if (!open) setTyped(''); }, [open]);
  if (!open) return null;
  const valid = typed === requireText;
  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 9998, background: 'rgba(15,23,42,0.45)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '20px',
    }} onClick={onCancel}>
      <div onClick={e => e.stopPropagation()} style={{
        background: '#fff', borderRadius: '10px', maxWidth: '440px', width: '100%',
        boxShadow: '0 16px 48px rgba(0,0,0,0.25)', border: `1px solid ${Tin.color.border.light}`,
      }}>
        <div style={{
          padding: '16px 20px', borderBottom: `1px solid ${Tin.color.border.light}`,
          fontSize: '13px', fontWeight: 700, color: destructive ? '#B91C1C' : Tin.color.text.primary,
        }}>{title}</div>
        <div style={{ padding: '16px 20px' }}>
          <div style={{ fontSize: '12px', color: Tin.color.text.secondary, lineHeight: 1.55, marginBottom: '12px' }}>{message}</div>
          <div style={{ fontSize: '11px', color: Tin.color.text.tertiary, marginBottom: '6px' }}>
            Type <code style={{ background: Tin.color.bg.secondary, padding: '1px 6px', borderRadius: '3px', fontFamily: Tin.font.mono, fontWeight: 700 }}>{requireText}</code> to confirm:
          </div>
          <input value={typed} onChange={e => setTyped(e.target.value)} autoFocus
            style={{
              width: '100%', padding: '8px 12px', borderRadius: '6px', fontSize: '12px',
              border: `1px solid ${valid ? '#059669' : Tin.color.border.medium}`,
              fontFamily: Tin.font.mono, outline: 'none', boxSizing: 'border-box',
            }} />
        </div>
        <div style={{
          padding: '12px 20px', borderTop: `1px solid ${Tin.color.border.light}`,
          display: 'flex', gap: '8px', justifyContent: 'flex-end', background: Tin.color.bg.secondary,
          borderRadius: '0 0 10px 10px',
        }}>
          <button onClick={onCancel} style={{
            padding: '7px 14px', borderRadius: '6px', border: `1px solid ${Tin.color.border.medium}`,
            background: '#fff', fontSize: '12px', fontWeight: 600, cursor: 'pointer',
          }}>Cancel</button>
          <button disabled={!valid} onClick={onConfirm} style={{
            padding: '7px 14px', borderRadius: '6px', border: 'none',
            background: valid ? '#B91C1C' : '#CBD5E1', color: '#fff',
            fontSize: '12px', fontWeight: 700, cursor: valid ? 'pointer' : 'not-allowed',
          }}>{destructive ? 'Delete' : 'Confirm'}</button>
        </div>
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// 7. AUDIT SIDE-PANEL ("who changed what") — #30
// ────────────────────────────────────────────────────────────────────────────
function AuditDrawer({ open, onClose, entry, history = [] }) {
  if (!open) return null;
  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 9997, background: 'rgba(15,23,42,0.35)',
      display: 'flex', justifyContent: 'flex-end',
    }} onClick={onClose}>
      <div onClick={e => e.stopPropagation()} style={{
        width: '420px', maxWidth: '100%', height: '100%', background: '#fff',
        boxShadow: '-8px 0 24px rgba(0,0,0,0.15)', display: 'flex', flexDirection: 'column',
      }}>
        <div style={{
          padding: '14px 18px', borderBottom: `1px solid ${Tin.color.border.light}`,
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        }}>
          <div>
            <div style={{ fontSize: '10px', fontWeight: 700, color: Tin.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Change history</div>
            <div style={{ fontSize: '14px', fontWeight: 700, color: Tin.color.text.primary, marginTop: '2px' }}>{entry?.label || 'Config item'}</div>
          </div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', fontSize: '18px', cursor: 'pointer', color: Tin.color.text.tertiary }}>×</button>
        </div>
        <div style={{ flex: 1, overflowY: 'auto', padding: '14px 18px' }}>
          {history.length === 0 ? (
            <EmptyState title="No history" hint="This configuration has not been modified." />
          ) : history.map((h, i) => (
            <div key={i} style={{
              padding: '10px 0', borderBottom: `1px solid ${Tin.color.border.light}`,
              display: 'grid', gridTemplateColumns: '28px 1fr', gap: '10px',
            }}>
              <div style={{
                width: '24px', height: '24px', borderRadius: '50%',
                background: '#334155', color: '#fff', fontSize: '10px', fontWeight: 700,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
              }}>{h.actor?.slice(0, 2).toUpperCase() || '??'}</div>
              <div>
                <div style={{ fontSize: '12px', fontWeight: 600, color: Tin.color.text.primary }}>{h.action}</div>
                <div style={{ fontSize: '11px', color: Tin.color.text.tertiary, marginTop: '2px' }}>
                  <span style={{ fontWeight: 600 }}>{h.actor}</span> · {h.at}
                </div>
                {h.before != null && h.after != null && (
                  <div style={{ fontSize: '11px', marginTop: '4px', display: 'flex', gap: '6px', alignItems: 'center', fontFamily: Tin.font.mono }}>
                    <span style={{ padding: '1px 6px', borderRadius: '3px', background: 'rgba(185,28,28,0.08)', color: '#B91C1C', textDecoration: 'line-through' }}>{String(h.before)}</span>
                    <span style={{ color: Tin.color.text.tertiary }}>→</span>
                    <span style={{ padding: '1px 6px', borderRadius: '3px', background: 'rgba(5,150,105,0.08)', color: '#059669' }}>{String(h.after)}</span>
                  </div>
                )}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// Canned sample histories (same shape for every row)
function sampleHistory(label) {
  return [
    { actor: 'Justin S.', action: `Enabled ${label}`,  at: '2026-04-22 14:02 UTC', before: 'off', after: 'on' },
    { actor: 'Amara R.',  action: `Reduced ${label} duration`, at: '2026-04-18 09:31 UTC', before: '720m', after: '480m' },
    { actor: 'System',    action: `Initial ${label} provisioning`, at: '2025-11-03 02:11 UTC' },
  ];
}

// ────────────────────────────────────────────────────────────────────────────
// 8. IMPACT PREVIEW (for role changes) — #25
// ────────────────────────────────────────────────────────────────────────────
function ImpactPill({ users = 0, teams = 0, matters = 0 }) {
  const total = users + teams + matters;
  if (total === 0) return null;
  const high = users > 10 || matters > 20;
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: '6px',
      padding: '3px 10px', borderRadius: '10px',
      background: high ? 'rgba(217,119,6,0.10)' : 'rgba(37,99,235,0.10)',
      color: high ? '#D97706' : '#2563EB',
      fontSize: '10px', fontWeight: 700,
    }}>
      <span></span>
      <span>Affects {users} users · {teams} teams · {matters} matters</span>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// 9. COMMAND PALETTE (cmdK search) — #1
// ────────────────────────────────────────────────────────────────────────────
function SettingsSearch({ open, onClose, items, onJump }) {
  const [q, setQ] = useInfraState('');
  const [idx, setIdx] = useInfraState(0);
  const inputRef = useInfraRef();

  useInfraEffect(() => { if (open) { setQ(''); setIdx(0); setTimeout(() => inputRef.current?.focus(), 0); } }, [open]);

  const filtered = useInfraMemo(() => {
    if (!q.trim()) return items.slice(0, 40);
    const needle = q.toLowerCase();
    return items.filter(i =>
      i.label.toLowerCase().includes(needle) ||
      i.group.toLowerCase().includes(needle) ||
      (i.tags || []).some(t => t.toLowerCase().includes(needle))
    ).slice(0, 40);
  }, [q, items]);

  useInfraEffect(() => { if (idx >= filtered.length) setIdx(0); }, [filtered, idx]);

  if (!open) return null;
  const handleKey = (e) => {
    if (e.key === 'Escape') onClose();
    else if (e.key === 'ArrowDown') { e.preventDefault(); setIdx(i => Math.min(filtered.length - 1, i + 1)); }
    else if (e.key === 'ArrowUp')   { e.preventDefault(); setIdx(i => Math.max(0, i - 1)); }
    else if (e.key === 'Enter')     { e.preventDefault(); if (filtered[idx]) { onJump(filtered[idx]); onClose(); } }
  };

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 9996, background: 'rgba(15,23,42,0.45)',
      display: 'flex', justifyContent: 'center', padding: '10vh 20px 20px',
    }} onClick={onClose}>
      <div onClick={e => e.stopPropagation()} style={{
        width: '580px', maxWidth: '100%', maxHeight: '70vh',
        background: '#fff', borderRadius: '10px', boxShadow: '0 20px 60px rgba(0,0,0,0.25)',
        display: 'flex', flexDirection: 'column', overflow: 'hidden',
      }}>
        <div style={{ padding: '10px 14px', borderBottom: `1px solid ${Tin.color.border.light}`, display: 'flex', alignItems: 'center', gap: '10px' }}>
          <span style={{ color: Tin.color.text.tertiary, fontSize: '14px' }}>cmd</span>
          <input ref={inputRef} value={q} onChange={e => setQ(e.target.value)} onKeyDown={handleKey}
            placeholder="Search settings — tabs, rows, rules, integrations…"
            style={{ flex: 1, border: 'none', outline: 'none', fontSize: '14px', fontFamily: Tin.font.family }} />
          <span style={{ fontSize: '10px', color: Tin.color.text.tertiary, fontFamily: Tin.font.mono }}>ESC</span>
        </div>
        <div style={{ flex: 1, overflowY: 'auto', padding: '6px 0' }}>
          {filtered.length === 0 ? (
            window.Arbiter?.EmptyState
              ? React.createElement(window.Arbiter.EmptyState, {
                  title: 'No results',
                  description: 'Try a different search term.',
                })
              : <div role="status" style={{ padding: '30px 20px', textAlign: 'center', fontSize: '12px', color: Tin.color.text.tertiary }}>No results</div>
          ) : filtered.map((item, i) => (
            <div key={item.group + item.sub + item.label}
              onMouseEnter={() => setIdx(i)}
              onClick={() => { onJump(item); onClose(); }}
              style={{
                padding: '8px 14px', cursor: 'pointer',
                background: i === idx ? 'rgba(51,65,85,0.08)' : 'transparent',
                display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '10px',
              }}>
              <div style={{ display: 'flex', flexDirection: 'column' }}>
                <div style={{ fontSize: '13px', fontWeight: 600, color: Tin.color.text.primary }}>{item.label}</div>
                <div style={{ fontSize: '10px', color: Tin.color.text.tertiary, marginTop: '1px' }}>
                  {item.group} › {item.sub}
                </div>
              </div>
              <div style={{ fontSize: '10px', color: Tin.color.text.tertiary, fontFamily: Tin.font.mono }}>{item.shortcut || '↵'}</div>
            </div>
          ))}
        </div>
        <div style={{
          padding: '8px 14px', borderTop: `1px solid ${Tin.color.border.light}`,
          background: Tin.color.bg.secondary, fontSize: '10px', color: Tin.color.text.tertiary,
          display: 'flex', gap: '14px',
        }}>
          <span>↑↓ navigate</span><span>↵ jump</span><span>esc close</span>
        </div>
      </div>
    </div>
  );
}

// Static search index — every surface + prominent rows
const SETTINGS_SEARCH_INDEX = [
  // Primary surfaces (20)
  { group: 'You', sub: 'Profile', label: 'Your profile', target: { primary: 'you', sub: 'profile' }, tags: ['avatar', 'bar admissions', 'contact'] },
  { group: 'You', sub: 'Account', label: 'Password, MFA, SSO, passkeys', target: { primary: 'you', sub: 'account' }, tags: ['password', 'mfa', '2fa', 'sso', 'passkey', 'api key', 'session'] },
  { group: 'You', sub: 'Preferences', label: 'Appearance, formats, workspace', target: { primary: 'you', sub: 'preferences' }, tags: ['dark mode', 'timezone', 'language'] },
  { group: 'You', sub: 'Notifications', label: 'Notification channels & rules', target: { primary: 'you', sub: 'notifications' }, tags: ['email', 'push', 'slack', 'quiet hours'] },
  { group: 'Firm', sub: 'Firm', label: 'Firm identity, offices, practice areas', target: { primary: 'firm', sub: 'firm' }, tags: ['office', 'branding', 'logo', 'practice'] },
  { group: 'Firm', sub: 'Users', label: 'Manage users & invitations', target: { primary: 'firm', sub: 'users' }, tags: ['invite', 'deactivate', 'scim', 'impersonate'] },
  { group: 'Firm', sub: 'Teams', label: 'Team membership & leads', target: { primary: 'firm', sub: 'teams' }, tags: ['team', 'lead'] },
  { group: 'Firm', sub: 'Roles', label: 'Roles & permission matrix', target: { primary: 'firm', sub: 'roles' }, tags: ['rbac', 'permission', 'ethical wall', 'conflict'] },
  { group: 'Security', sub: 'Security', label: 'Security policies, IP allowlist, encryption', target: { primary: 'security', sub: 'security' }, tags: ['mfa', 'session', 'ip', 'device', 'sso'] },
  { group: 'Security', sub: 'Audit Log', label: 'Activity & audit events', target: { primary: 'security', sub: 'audit' }, tags: ['log', 'event', 'siem', 'export'] },
  { group: 'Security', sub: 'Compliance', label: 'SOC2, ISO, HIPAA, GDPR frameworks', target: { primary: 'security', sub: 'compliance' }, tags: ['soc', 'iso', 'hipaa', 'gdpr', 'framework'] },
  { group: 'Integrations', sub: 'Integrations', label: '22 integrations', target: { primary: 'integrations', sub: 'integrations' }, tags: ['connect', 'oauth', 'clio', 'netdocuments', 'relativity'] },
  { group: 'Integrations', sub: 'API & Webhooks', label: 'API keys, webhooks, DLQ', target: { primary: 'integrations', sub: 'api' }, tags: ['api', 'webhook', 'token', 'dlq'] },
  { group: 'Integrations', sub: 'Email', label: 'SMTP, DNS, signatures', target: { primary: 'integrations', sub: 'email' }, tags: ['smtp', 'spf', 'dkim', 'dmarc', 'calendar'] },
  { group: 'Data', sub: 'Retention', label: 'Retention, legal holds, exports', target: { primary: 'data', sub: 'retention' }, tags: ['retention', 'hold', 'export', 'purge'] },
  { group: 'Data', sub: 'Custom Fields', label: 'Custom fields for matters, docs, entities', target: { primary: 'data', sub: 'fields' }, tags: ['field', 'schema'] },
  { group: 'Data', sub: 'Templates', label: 'Document & email templates', target: { primary: 'data', sub: 'templates' }, tags: ['template'] },
  { group: 'Data', sub: 'Import / Export', label: 'Data import & export jobs', target: { primary: 'data', sub: 'importexport' }, tags: ['import', 'export', 'backup'] },
  { group: 'System', sub: 'Billing', label: 'Plan, usage, invoices, add-ons', target: { primary: 'system', sub: 'billing' }, tags: ['plan', 'invoice', 'seat', 'usage'] },
  { group: 'System', sub: 'System Health', label: 'Service status, quotas, regions, incidents', target: { primary: 'system', sub: 'health' }, tags: ['status', 'uptime', 'incident', 'region'] },
  // Selected deep-rows
  { group: 'Security', sub: 'Security', label: 'Configure SAML SSO', target: { primary: 'security', sub: 'security', anchor: 'sso' }, tags: ['sso', 'saml', 'idp'] },
  { group: 'Security', sub: 'Security', label: 'Password policy', target: { primary: 'security', sub: 'security', anchor: 'password' }, tags: ['password', 'complexity', 'rotation'] },
  { group: 'Firm', sub: 'Users', label: 'Invite user', target: { primary: 'firm', sub: 'users', anchor: 'invite' }, tags: ['invite', 'new user'] },
  { group: 'Integrations', sub: 'API & Webhooks', label: 'Send test webhook', target: { primary: 'integrations', sub: 'api', anchor: 'test' }, tags: ['test', 'webhook'] },
  { group: 'Data', sub: 'Retention', label: 'Legal hold workflow', target: { primary: 'data', sub: 'retention', anchor: 'hold' }, tags: ['hold', 'custodian', 'notice'] },
];

// ────────────────────────────────────────────────────────────────────────────
// 10. HASH ROUTER — deep-linkable — #28
// ────────────────────────────────────────────────────────────────────────────
function parseSettingsHash() {
  const h = (typeof window !== 'undefined' ? window.location.hash : '') || '';
  // Expected format: #/settings/<primary>/<sub>?anchor=xxx
  const m = h.match(/#\/settings\/([^/?#]+)(?:\/([^/?#]+))?/);
  if (!m) return null;
  return { primary: m[1], sub: m[2] };
}

function setSettingsHash(primary, sub) {
  if (typeof window === 'undefined') return;
  const next = `#/settings/${primary}/${sub}`;
  if (window.location.hash !== next) history.replaceState(null, '', next);
}

// ────────────────────────────────────────────────────────────────────────────
// 11. COMPLIANCE BREAKDOWN DRAWER — #31
// ────────────────────────────────────────────────────────────────────────────
function ComplianceBreakdownDrawer({ open, onClose }) {
  if (!open) return null;
  const data = window.SETTINGS_DATA?.compliance || [];
  const avg = data.length ? Math.round(data.reduce((s, f) => s + (f.score || 0), 0) / data.length) : 0;
  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 9995, background: 'rgba(15,23,42,0.35)', display: 'flex', justifyContent: 'flex-end' }} onClick={onClose}>
      <div onClick={e => e.stopPropagation()} style={{ width: '460px', maxWidth: '100%', height: '100%', background: '#fff', boxShadow: '-8px 0 24px rgba(0,0,0,0.15)', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '14px 18px', borderBottom: `1px solid ${Tin.color.border.light}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div>
            <div style={{ fontSize: '10px', fontWeight: 700, color: Tin.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Compliance score</div>
            <div style={{ fontSize: '24px', fontWeight: 800, color: '#7C3AED', marginTop: '2px' }}>{avg}%</div>
          </div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', fontSize: '18px', cursor: 'pointer' }}>×</button>
        </div>
        <div style={{ flex: 1, overflowY: 'auto', padding: '14px 18px' }}>
          {data.length === 0 ? <EmptyState title="No frameworks configured" /> : data.map(f => (
            <div key={f.name} style={{ padding: '10px 0', borderBottom: `1px solid ${Tin.color.border.light}` }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
                <div style={{ fontSize: '13px', fontWeight: 700, color: Tin.color.text.primary }}>{f.name}</div>
                <div style={{ fontSize: '12px', fontWeight: 700, color: f.score >= 95 ? '#059669' : f.score >= 80 ? '#D97706' : '#B91C1C', fontFamily: Tin.font.mono }}>{f.score}%</div>
              </div>
              <div style={{ height: '4px', borderRadius: '2px', background: Tin.color.bg.secondary, overflow: 'hidden', marginBottom: '4px' }}>
                <div style={{ height: '100%', width: `${f.score}%`, background: f.score >= 95 ? '#059669' : f.score >= 80 ? '#D97706' : '#B91C1C' }} />
              </div>
              <div style={{ fontSize: '10px', color: Tin.color.text.tertiary }}>{f.controlsPassing || 0}/{f.controlsTotal || 0} controls · next audit {f.nextAudit || '—'}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// 12. SETTINGS-AS-CODE EXPORT — #33
// ────────────────────────────────────────────────────────────────────────────
function downloadSettingsAsCode() {
  const data = window.SETTINGS_DATA || {};
  const out = {
    version: '1.0.0',
    exportedAt: new Date().toISOString(),
    tenant: data.firm?.name || 'Arbiter',
    firm: data.firm,
    security: data.security,
    roles: data.roles,
    retention: data.retention,
    compliance: data.compliance,
    integrations: (data.integrations || []).map(i => ({ id: i.id, name: i.name, category: i.category, enabled: i.status === 'ok' })),
    email: data.email,
    customFields: data.customFields,
    // Never include: users, api keys, session tokens, webhooks' shared secrets
  };
  const yaml = toYaml(out);
  const blob = new Blob([yaml], { type: 'text/yaml' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = `arbiter-settings-${new Date().toISOString().slice(0, 10)}.yaml`;
  document.body.appendChild(a); a.click(); document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

function toYaml(v, indent = 0) {
  const pad = '  '.repeat(indent);
  if (v === null || v === undefined) return 'null';
  if (typeof v === 'string') return JSON.stringify(v);
  if (typeof v === 'number' || typeof v === 'boolean') return String(v);
  if (Array.isArray(v)) {
    if (v.length === 0) return '[]';
    return '\n' + v.map(item => pad + '- ' + (typeof item === 'object' ? toYaml(item, indent + 1).replace(/^\n/, '') : toYaml(item, indent + 1))).join('\n');
  }
  if (typeof v === 'object') {
    const keys = Object.keys(v);
    if (keys.length === 0) return '{}';
    return (indent === 0 ? '' : '\n') + keys.map(k => pad + k + ': ' + toYaml(v[k], indent + 1)).join('\n');
  }
  return String(v);
}

// ────────────────────────────────────────────────────────────────────────────
// 13. BULK-SELECT HEAD ROW — used by Users table — #2
// ────────────────────────────────────────────────────────────────────────────
function BulkBar({ count, actions = [] }) {
  if (count === 0) return null;
  return (
    <div style={{
      padding: '10px 14px', borderRadius: '8px',
      background: 'rgba(37,99,235,0.08)', border: '1px solid rgba(37,99,235,0.22)',
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      marginBottom: '12px', fontSize: '12px',
    }}>
      <div style={{ color: '#1E40AF', fontWeight: 700 }}>{count} selected</div>
      <div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}>
        {actions.map((a, i) => (
          <button key={i} onClick={a.onClick} style={{
            padding: '5px 10px', borderRadius: '5px', border: `1px solid ${a.danger ? 'rgba(185,28,28,0.3)' : 'rgba(37,99,235,0.3)'}`,
            background: '#fff', color: a.danger ? '#B91C1C' : '#2563EB',
            fontSize: '11px', fontWeight: 600, cursor: 'pointer',
          }}>{a.label}</button>
        ))}
      </div>
    </div>
  );
}

// Expose everything
window.__stg_infra = {
  ToastProvider, useToast,
  DirtyProvider, useDirty, DirtyFooter,
  InfoTip, EmptyState, Skeleton,
  DangerZone, TypedConfirmDialog,
  AuditDrawer, sampleHistory,
  ImpactPill,
  SettingsSearch, SETTINGS_SEARCH_INDEX,
  parseSettingsHash, setSettingsHash,
  ComplianceBreakdownDrawer,
  downloadSettingsAsCode,
  BulkBar,
};
