// Arbiter Primitives — enterprise UI building blocks.
// Registered globally on `window.Arbiter.*` so every component can share them without imports.
//
// Covers opportunities:
//   #3 Dark mode          → ThemeSwitch, useTheme
//   #4 Density             → DensitySwitch
//   #6 Icon a11y           → IconA11y helper
//   #7 Keyboard nav        → useFocusTrap, useArrowRoving, useEscape
//   #10 SR semantics       → VisuallyHidden, StatusPill (text + glyph + sr text)
//   #11 Empty/loading/err  → EmptyState, ErrorBoundary, Skeleton, SkeletonRow
//   #12 Virtualization     → VirtualList (fixed-height row)
//   #14 Optimistic         → useOptimistic
//   #15 Command palette    → CommandRegistry, useCommand
//   #16 Breadcrumb         → Breadcrumb, useBackStack
//   #17 Saved views        → useSavedViews
//   #18 Toasts             → ToastHost, toast()
//   #20 RBAC               → Gated, usePermission
//   #21 i18n               → t(), I18n, registerMessages
//   #23 Audit overlay      → AuditChip
//   #24 Export             → ExportButton (CSV, JSON, print)
//   #25 Telemetry          → useTrack, track()

(function () {
  const R = window.React;
  if (!R) { console.warn('[Arbiter] React missing; Primitives disabled'); return; }
  const { useEffect, useState, useRef, useCallback, useMemo, useLayoutEffect, createContext, useContext } = R;
  const T = window.ArbiterTokens;
  const Theme = window.ArbiterTheme;

  // ── Visually hidden (screen-reader text) ────────────────────────────────
  const VisuallyHidden = ({ children, as:Tag='span' }) =>
    R.createElement(Tag, { className:'sr-only' }, children);

  // ── Icon a11y wrapper ───────────────────────────────────────────────────
  // Wrap any Icons.X with decorative or labeled semantics.
  //   <IconA11y label="Delete"><Icons.Trash /></IconA11y>
  //   <IconA11y decorative><Icons.Star /></IconA11y>
  const IconA11y = ({ label, decorative, children }) => {
    const extraProps = decorative
      ? { 'aria-hidden':'true', focusable:'false' }
      : { role:'img', 'aria-label': label || 'icon' };
    return R.createElement('span', { style:{display:'inline-flex'}, ...extraProps }, children);
  };

  // ── Theme & density switches ────────────────────────────────────────────
  const useTheme = () => {
    const [mode, setLocal] = useState(Theme ? Theme.mode : 'light');
    const [dens, setDens]  = useState(Theme ? Theme.density : 'comfortable');
    useEffect(() => Theme && Theme.subscribe(s => { setLocal(s.mode); setDens(s.density); }), []);
    return { mode, density:dens, setMode:Theme.setMode, setDensity:Theme.setDensity };
  };
  const ThemeSwitch = () => {
    const { mode, setMode } = useTheme();
    return R.createElement('button', {
      type:'button', 'aria-label':`Switch to ${mode==='dark'?'light':'dark'} mode`,
      onClick:() => setMode(mode==='dark'?'light':'dark'),
      style:{ background:'transparent', border:`1px solid var(--color-border-medium)`,
              borderRadius:'6px', padding:'6px 10px', cursor:'pointer',
              color:'var(--color-text-secondary)', fontSize:'12px' }
    }, mode==='dark' ? 'Light' : 'Dark');
  };
  const DensitySwitch = () => {
    const { density, setDensity } = useTheme();
    return R.createElement('select', {
      'aria-label':'Density', value:density,
      onChange:e => setDensity(e.target.value),
      style:{ background:'transparent', border:`1px solid var(--color-border-medium)`,
              borderRadius:'6px', padding:'6px 8px', color:'var(--color-text-secondary)',
              fontSize:'12px' }
    }, [
      R.createElement('option', { key:'c', value:'compact' }, 'Compact'),
      R.createElement('option', { key:'n', value:'comfortable' }, 'Comfortable'),
      R.createElement('option', { key:'t', value:'touch' }, 'Touch'),
    ]);
  };

  // ── Keyboard helpers ────────────────────────────────────────────────────
  const useEscape = (onEsc) => useEffect(() => {
    const h = e => { if (e.key === 'Escape') onEsc(e); };
    document.addEventListener('keydown', h); return () => document.removeEventListener('keydown', h);
  }, [onEsc]);

  const useFocusTrap = (active) => {
    const ref = useRef(null);
    useEffect(() => {
      if (!active || !ref.current) return;
      const root = ref.current;
      const getFocusables = () => Array.from(root.querySelectorAll(
        'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])'
      ));
      const first = () => getFocusables()[0];
      first()?.focus();
      const onKey = e => {
        if (e.key !== 'Tab') return;
        const items = getFocusables(); if (!items.length) return;
        const idx = items.indexOf(document.activeElement);
        if (e.shiftKey && idx <= 0) { items[items.length-1].focus(); e.preventDefault(); }
        else if (!e.shiftKey && idx === items.length-1) { items[0].focus(); e.preventDefault(); }
      };
      root.addEventListener('keydown', onKey); return () => root.removeEventListener('keydown', onKey);
    }, [active]);
    return ref;
  };

  const useArrowRoving = (orientation='vertical') => {
    const ref = useRef(null);
    useEffect(() => {
      const root = ref.current; if (!root) return;
      const keys = orientation==='vertical'
        ? { next:'ArrowDown', prev:'ArrowUp' }
        : { next:'ArrowRight', prev:'ArrowLeft' };
      const onKey = e => {
        if (![keys.next, keys.prev, 'Home', 'End'].includes(e.key)) return;
        const items = Array.from(root.querySelectorAll('[role="option"],[role="menuitem"],[data-roving]'));
        if (!items.length) return;
        const cur = items.indexOf(document.activeElement);
        let nxt = cur;
        if (e.key === keys.next) nxt = (cur + 1) % items.length;
        if (e.key === keys.prev) nxt = (cur - 1 + items.length) % items.length;
        if (e.key === 'Home') nxt = 0;
        if (e.key === 'End')  nxt = items.length - 1;
        items[nxt]?.focus(); e.preventDefault();
      };
      root.addEventListener('keydown', onKey); return () => root.removeEventListener('keydown', onKey);
    }, [orientation]);
    return ref;
  };

  // ── Empty / error / skeleton states ─────────────────────────────────────
  const EmptyState = ({ title='Nothing here yet', description, action, icon }) =>
    R.createElement('div', {
      role:'status',
      style:{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center',
              padding:'48px 24px', textAlign:'center', color:'var(--color-text-secondary)',
              background:'var(--color-bg-card)', border:`1px dashed var(--color-border-medium)`,
              borderRadius:'8px' }
    }, [
      icon && R.createElement('div', { key:'i', style:{ marginBottom:'12px', opacity:0.7 } }, icon),
      R.createElement('div', { key:'t', style:{ fontSize:'14px', fontWeight:600, color:'var(--color-text-primary)', marginBottom:'4px' } }, title),
      description && R.createElement('div', { key:'d', style:{ fontSize:'12px', maxWidth:'360px' } }, description),
      action && R.createElement('div', { key:'a', style:{ marginTop:'16px' } }, action),
    ]);

  class ErrorBoundary extends R.Component {
    constructor(p) { super(p); this.state = { err:null }; }
    static getDerivedStateFromError(err) { return { err }; }
    componentDidCatch(err, info) { if (window.Arbiter?.track) window.Arbiter.track('error', { message:String(err), stack:info?.componentStack }); }
    render() {
      if (!this.state.err) return this.props.children;
      return R.createElement('div', {
        role:'alert',
        style:{ padding:'16px', border:`1px solid var(--color-status-critical)`, borderRadius:'8px',
                background:'var(--color-status-critical-bg)', color:'var(--color-status-critical)' }
      }, [
        R.createElement('strong', { key:'s' }, 'Something went wrong. '),
        R.createElement('span',    { key:'m' }, String(this.state.err?.message || this.state.err)),
        R.createElement('button', {
          key:'r', onClick:() => this.setState({ err:null }),
          style:{ marginLeft:12, background:'transparent', border:`1px solid currentColor`,
                  color:'inherit', borderRadius:'4px', padding:'4px 10px', cursor:'pointer' }
        }, 'Retry'),
      ]);
    }
  }

  const Skeleton = ({ width='100%', height=14, radius=4, style }) =>
    R.createElement('span', {
      'aria-hidden':'true',
      style:{ display:'inline-block', width, height, borderRadius:radius,
              background:'linear-gradient(90deg,var(--color-bg-tertiary),var(--color-bg-secondary),var(--color-bg-tertiary))',
              backgroundSize:'200% 100%', animation:'arbiter-shimmer 1.4s ease-in-out infinite',
              ...style }
    });
  const SkeletonRow = ({ cols=5 }) =>
    R.createElement('div', { style:{ display:'grid', gap:'8px', gridTemplateColumns:`repeat(${cols},1fr)`, padding:'10px 16px' } },
      Array.from({ length:cols }, (_,i) => R.createElement(Skeleton, { key:i, height:12 })));

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

  // ── Status pill (screen-reader safe) ────────────────────────────────────
  const StatusPill = ({ kind='pending', label, children }) => {
    const color = `var(--color-status-${kind})`;
    const bg    = `var(--color-status-${kind}-bg)`;
    return R.createElement('span', {
      role:'status',
      style:{ display:'inline-flex', alignItems:'center', gap:'6px', padding:'2px 8px',
              borderRadius:'10px', fontSize:'11px', fontWeight:500, color, background:bg }
    }, [
      R.createElement('span', { key:'d', 'aria-hidden':'true',
        style:{ width:6, height:6, borderRadius:'50%', background:color, display:'inline-block' } }),
      R.createElement('span', { key:'l' }, children || label),
    ]);
  };

  // ── Virtual list (fixed row height) ─────────────────────────────────────
  const VirtualList = ({ items, rowHeight=36, height=480, render, overscan=6 }) => {
    const [scroll, setScroll] = useState(0);
    const total = items.length * rowHeight;
    const start = Math.max(0, Math.floor(scroll / rowHeight) - overscan);
    const end   = Math.min(items.length, Math.ceil((scroll + height) / rowHeight) + overscan);
    const visible = items.slice(start, end);
    return R.createElement('div', {
      onScroll:e => setScroll(e.currentTarget.scrollTop),
      style:{ height, overflow:'auto', position:'relative' }
    }, R.createElement('div', { style:{ height:total, position:'relative' } },
      visible.map((item, i) => R.createElement('div', {
        key:start+i,
        style:{ position:'absolute', top:(start+i)*rowHeight, left:0, right:0, height:rowHeight }
      }, render(item, start+i)))
    ));
  };

  // ── Optimistic updates ──────────────────────────────────────────────────
  const useOptimistic = (initial) => {
    const [state, set] = useState(initial);
    const snapshot = useRef(initial);
    const apply = useCallback(async (mutator, commit) => {
      snapshot.current = state;
      set(mutator(state));
      try { const result = await commit(); if (result !== undefined) set(result); }
      catch (err) { set(snapshot.current); throw err; }
    }, [state]);
    return [state, apply, set];
  };

  // ── Command registry (cmdK palette backend) ───────────────────────────────
  const commands = new Map();
  const commandListeners = new Set();
  const CommandRegistry = {
    register(id, def) { commands.set(id, { id, ...def }); commandListeners.forEach(f=>f()); return () => { commands.delete(id); commandListeners.forEach(f=>f()); }; },
    unregister(id)    { commands.delete(id); commandListeners.forEach(f=>f()); },
    list()            { return Array.from(commands.values()); },
    subscribe(fn)     { commandListeners.add(fn); return () => commandListeners.delete(fn); },
    run(id, ctx)      { const c = commands.get(id); if (c?.run) return c.run(ctx); },
  };
  const useCommand = (id, def, deps=[]) => useEffect(() => CommandRegistry.register(id, def), deps);

  // ── Back-stack / breadcrumb ─────────────────────────────────────────────
  const backStack = [];
  const Breadcrumb = ({ items=[] }) =>
    R.createElement('nav', { 'aria-label':'Breadcrumb', style:{ fontSize:'12px', color:'var(--color-text-tertiary)' } },
      R.createElement('ol', { style:{ display:'flex', gap:'6px', listStyle:'none', padding:0, margin:0, flexWrap:'wrap' } },
        items.map((it, i) => R.createElement('li', {
          key:i, style:{ display:'flex', alignItems:'center', gap:'6px' }
        }, [
          i>0 && R.createElement('span', { key:'sep', 'aria-hidden':'true' }, '/'),
          it.href
            ? R.createElement('a', { key:'l', href:it.href, onClick:it.onClick,
                style:{ color:'inherit', textDecoration:'none' } }, it.label)
            : R.createElement('span', { key:'c', 'aria-current':'page',
                style:{ color:'var(--color-text-primary)', fontWeight:500 } }, it.label)
        ]))));
  const useBackStack = () => ({ push:(x)=>backStack.push(x), pop:()=>backStack.pop(), peek:()=>backStack[backStack.length-1] });

  // ── Saved views (inline filter chips + permalink) ───────────────────────
  const useSavedViews = (key) => {
    const lsKey = `arbiter:view:${key}`;
    const [views, setViews] = useState(() => {
      try { return JSON.parse(localStorage.getItem(lsKey) || '[]'); } catch { return []; }
    });
    const save = useCallback(v => {
      const next = [...views.filter(x => x.name !== v.name), v];
      setViews(next); try { localStorage.setItem(lsKey, JSON.stringify(next)); } catch {}
    }, [views, lsKey]);
    const remove = useCallback(name => {
      const next = views.filter(v => v.name !== name);
      setViews(next); try { localStorage.setItem(lsKey, JSON.stringify(next)); } catch {}
    }, [views, lsKey]);
    return { views, save, remove };
  };

  // ── Toast host ──────────────────────────────────────────────────────────
  let toastDispatch = null;
  const ToastHost = () => {
    const [items, setItems] = useState([]);
    toastDispatch = (t) => {
      const id = Math.random().toString(36).slice(2);
      setItems(xs => [...xs, { id, ...t }]);
      if (t.duration !== 0) setTimeout(() => setItems(xs => xs.filter(x => x.id !== id)), t.duration || 4000);
    };
    return R.createElement('div', {
      role:'status', 'aria-live':'polite', 'aria-atomic':'true',
      style:{ position:'fixed', bottom:20, right:20, zIndex:T.z?.toast || 1200,
              display:'flex', flexDirection:'column', gap:8, maxWidth:360 }
    }, items.map(it => R.createElement('div', {
      key:it.id,
      style:{ background:'var(--color-bg-card)', border:`1px solid var(--color-border-medium)`,
              borderLeft:`3px solid var(--color-status-${it.kind||'pending'})`,
              padding:'10px 14px', borderRadius:'8px', boxShadow:'var(--shadow-md)',
              color:'var(--color-text-primary)', fontSize:'13px' }
    }, [
      it.title && R.createElement('div', { key:'t', style:{ fontWeight:600, marginBottom:2 } }, it.title),
      it.message && R.createElement('div', { key:'m', style:{ color:'var(--color-text-secondary)', fontSize:'12px' } }, it.message),
    ])));
  };
  const toast = (t) => { if (toastDispatch) toastDispatch(t); else console.info('[toast]', t); };

  // ── RBAC gated rendering ────────────────────────────────────────────────
  let permissionResolver = () => true; // default allow
  const configurePermissions = fn => { permissionResolver = fn; };
  const usePermission = (perm) => useMemo(() => permissionResolver(perm), [perm]);
  const Gated = ({ permission, fallback=null, children }) =>
    permissionResolver(permission) ? children : fallback;

  // ── i18n (message catalog) ──────────────────────────────────────────────
  const messages = { 'en-US': {} };
  let locale = (typeof navigator !== 'undefined' && navigator.language) || 'en-US';
  const registerMessages = (loc, msgs) => { messages[loc] = Object.assign(messages[loc] || {}, msgs); };
  const setLocale = (l) => { locale = l; };
  const t = (key, vars) => {
    const raw = messages[locale]?.[key] ?? messages['en-US']?.[key] ?? key;
    return vars ? raw.replace(/\{(\w+)\}/g, (_,k) => vars[k] ?? '') : raw;
  };

  // ── Telemetry ───────────────────────────────────────────────────────────
  const trackSinks = [(ev, props) => console.debug('[track]', ev, props)];
  const track = (event, props={}) =>
    trackSinks.forEach(sink => { try { sink(event, { ts:Date.now(), ...props }); } catch {} });
  const useTrack = (event, props, deps=[]) => useEffect(() => track(event, props), deps);
  const addTrackSink = fn => trackSinks.push(fn);

  // ── Audit chip (universal "who touched this, when") ─────────────────────
  const AuditChip = ({ who, when, title='Last modified' }) =>
    R.createElement('span', {
      title:`${title} by ${who} at ${when}`,
      style:{ display:'inline-flex', gap:4, alignItems:'center', padding:'2px 8px',
              borderRadius:'10px', background:'var(--color-bg-tertiary)',
              color:'var(--color-text-tertiary)', fontSize:'11px' }
    }, [
      R.createElement('span', { key:'w', 'aria-hidden':'true' }, '•'),
      R.createElement('span', { key:'t' }, `${who} · ${when}`),
    ]);

  // ── Export button (CSV / JSON / print) ──────────────────────────────────
  const toCsv = (rows) => {
    if (!rows?.length) return '';
    const cols = Object.keys(rows[0]);
    const esc = v => { const s = v==null?'':String(v); return /[",\n]/.test(s) ? `"${s.replace(/"/g,'""')}"` : s; };
    return [cols.join(','), ...rows.map(r => cols.map(c => esc(r[c])).join(','))].join('\n');
  };
  const download = (name, mime, content) => {
    const blob = new Blob([content], { type:mime });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a'); a.href = url; a.download = name;
    document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
  };
  const ExportButton = ({ rows, filename='export', formats=['csv','json'] }) => {
    const [open, setOpen] = useState(false);
    useEscape(() => setOpen(false));
    const doExport = fmt => {
      track('export', { filename, format:fmt, count:rows?.length });
      if (fmt==='csv')  download(`${filename}.csv`,  'text/csv;charset=utf-8', toCsv(rows));
      if (fmt==='json') download(`${filename}.json`, 'application/json', JSON.stringify(rows, null, 2));
      if (fmt==='print') window.print();
      setOpen(false);
    };
    return R.createElement('div', { style:{ position:'relative', display:'inline-block' } }, [
      R.createElement('button', {
        key:'b', type:'button', 'aria-haspopup':'menu', 'aria-expanded':open,
        onClick:() => setOpen(x=>!x),
        style:{ background:'transparent', border:`1px solid var(--color-border-medium)`,
                borderRadius:'6px', padding:'6px 10px', fontSize:'12px', cursor:'pointer',
                color:'var(--color-text-secondary)' }
      }, 'Export'),
      open && R.createElement('div', {
        key:'m', role:'menu',
        style:{ position:'absolute', right:0, top:'calc(100% + 4px)', zIndex:100,
                background:'var(--color-bg-card)', border:`1px solid var(--color-border-medium)`,
                borderRadius:'6px', boxShadow:'var(--shadow-md)', minWidth:140 }
      }, formats.concat('print').map(f => R.createElement('button', {
        key:f, role:'menuitem', onClick:() => doExport(f),
        style:{ display:'block', width:'100%', textAlign:'left', background:'transparent',
                border:0, padding:'8px 12px', fontSize:'12px', cursor:'pointer',
                color:'var(--color-text-primary)' }
      }, f.toUpperCase())))
    ]);
  };

  // ── Global print stylesheet (opp #22) ───────────────────────────────────
  if (typeof document !== 'undefined' && !document.getElementById('arbiter-print')) {
    const s = document.createElement('style'); s.id = 'arbiter-print';
    s.textContent = `
      @media print {
        .no-print, nav[aria-label="Primary"], .tweaks-panel, .toast-host { display:none !important; }
        body { background:#fff !important; color:#000 !important; }
        * { box-shadow:none !important; }
        a[href]::after { content:" (" attr(href) ")"; font-size:10px; color:#555; }
      }`;
    document.head.appendChild(s);
  }

  // ── Global export ───────────────────────────────────────────────────────
  window.Arbiter = Object.assign(window.Arbiter || {}, {
    VisuallyHidden, IconA11y, ThemeSwitch, DensitySwitch, useTheme,
    useEscape, useFocusTrap, useArrowRoving,
    EmptyState, ErrorBoundary, Skeleton, SkeletonRow, StatusPill,
    VirtualList, useOptimistic,
    CommandRegistry, useCommand,
    Breadcrumb, useBackStack,
    useSavedViews,
    ToastHost, toast,
    Gated, usePermission, configurePermissions,
    t, registerMessages, setLocale,
    track, useTrack, addTrackSink,
    AuditChip, ExportButton,
  });
})();
