// Arbiter — ContextMenu primitive.
// Reusable right-click menu with keyboard navigation, viewport clamping,
// submenus, icons, dividers, disabled items, danger styling, and a
// promise-style `open(event, items)` API.
//
// Contract:
//   Arbiter.ContextMenu.open(event, items, options?)
//     items:   Array of MenuItem | { separator: true } | { heading: string }
//     options: { source?: 'canvas' | …, onClose?: () => void }
//
//   MenuItem:
//     { label, icon?, hint?, danger?, disabled?, onSelect?(), submenu? }
//
// Mount once: <Arbiter.ContextMenuHost /> at the app root.
// Events: cancels on Esc, outside click, scroll, route change.

(function () {
  const R = window.React;
  if (!R) return;
  const { useState, useEffect, useRef, useCallback, useLayoutEffect } = R;
  const T = window.ArbiterTokens;

  // ── Shared state — one active menu at a time ────────────────────────────
  let openFn = null;
  let closeFn = null;

  function openMenu(event, items, options = {}) {
    if (!openFn) { console.warn('[ContextMenu] Host not mounted'); return; }
    event.preventDefault?.();
    event.stopPropagation?.();
    const x = event.clientX ?? event.pageX ?? 0;
    const y = event.clientY ?? event.pageY ?? 0;
    openFn({ x, y, items: normalize(items), options });
  }

  function normalize(items) {
    return (items || []).filter(Boolean).map((it, i) => ({
      __id: it.__id || `mi-${i}-${Math.random().toString(36).slice(2, 6)}`,
      ...it,
    }));
  }

  // ── Host: one menu + possibly one submenu ───────────────────────────────
  function ContextMenuHost() {
    const [state, setState] = useState(null);
    // state: { x, y, items, options } | null
    openFn = setState;
    closeFn = () => { setState(null); };

    useEffect(() => {
      if (!state) return;
      const onKey = (e) => { if (e.key === 'Escape') closeFn(); };
      const onScroll = () => closeFn();
      const onResize = () => closeFn();
      // Close on outside click. React's synthetic stopPropagation doesn't
      // stop native events, so check the target against live DOM menus.
      const onDown = (e) => {
        const inside = e.target && e.target.closest && e.target.closest('[data-arbiter-cm]');
        if (!inside) closeFn();
      };
      window.addEventListener('keydown', onKey);
      window.addEventListener('scroll', onScroll, true);
      window.addEventListener('resize', onResize);
      window.addEventListener('mousedown', onDown, true);
      return () => {
        window.removeEventListener('keydown', onKey);
        window.removeEventListener('scroll', onScroll, true);
        window.removeEventListener('resize', onResize);
        window.removeEventListener('mousedown', onDown, true);
        state?.options?.onClose?.();
      };
    }, [state]);

    if (!state) return null;
    return R.createElement(Menu, {
      key: `${state.x},${state.y}`,
      x: state.x, y: state.y,
      items: state.items,
      onClose: closeFn,
      rootLabel: 'Context menu',
    });
  }

  // ── Single Menu (root or submenu) ───────────────────────────────────────
  function Menu({ x, y, items, onClose, parentRect, rootLabel }) {
    const ref = useRef(null);
    const itemRefs = useRef({});
    const [pos, setPos] = useState({ left: x, top: y });
    const [focusIdx, setFocusIdx] = useState(-1);
    const [openSubIdx, setOpenSubIdx] = useState(null);
    const [subRect, setSubRect] = useState(null);

    // Capture the trigger item's rect whenever a submenu opens.
    useLayoutEffect(() => {
      if (openSubIdx == null) { setSubRect(null); return; }
      const el = itemRefs.current[openSubIdx];
      if (el) setSubRect(el.getBoundingClientRect());
    }, [openSubIdx]);

    // Viewport clamping — run after first layout.
    useLayoutEffect(() => {
      const el = ref.current;
      if (!el) return;
      const rect = el.getBoundingClientRect();
      const pad = 8;
      const maxX = window.innerWidth - rect.width - pad;
      const maxY = window.innerHeight - rect.height - pad;
      let nx = x, ny = y;
      if (parentRect) {
        // Submenu: prefer right side of parent; flip if off-screen.
        nx = parentRect.right + 2;
        ny = parentRect.top;
        if (nx + rect.width > window.innerWidth - pad) nx = parentRect.left - rect.width - 2;
      }
      nx = Math.max(pad, Math.min(maxX, nx));
      ny = Math.max(pad, Math.min(maxY, ny));
      setPos({ left: nx, top: ny });
    }, [x, y, parentRect, items]);

    // Keyboard navigation
    useEffect(() => {
      const onKey = (e) => {
        const visible = items.map((it, i) => ({ it, i })).filter(({ it }) => !it.separator && !it.heading && !it.disabled);
        if (!visible.length) return;
        const curVisibleIdx = visible.findIndex(v => v.i === focusIdx);
        if (e.key === 'ArrowDown') {
          e.preventDefault();
          const nxt = visible[(curVisibleIdx + 1 + visible.length) % visible.length];
          setFocusIdx(nxt.i);
        } else if (e.key === 'ArrowUp') {
          e.preventDefault();
          const prv = visible[(curVisibleIdx - 1 + visible.length) % visible.length];
          setFocusIdx(prv.i);
        } else if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          const it = items[focusIdx];
          if (it && !it.disabled && !it.separator && !it.heading) activate(it, focusIdx);
        } else if (e.key === 'ArrowRight') {
          const it = items[focusIdx];
          if (it?.submenu?.length) { e.preventDefault(); setOpenSubIdx(focusIdx); }
        } else if (e.key === 'ArrowLeft') {
          if (openSubIdx != null) { e.preventDefault(); setOpenSubIdx(null); }
        }
      };
      window.addEventListener('keydown', onKey, true);
      return () => window.removeEventListener('keydown', onKey, true);
    }, [items, focusIdx, openSubIdx]);

    const activate = useCallback((it, idx) => {
      if (it.submenu?.length) {
        setOpenSubIdx(idx);
        return;
      }
      try { it.onSelect?.(); } finally { onClose?.(); }
    }, [onClose]);

    return R.createElement(R.Fragment, null,
      R.createElement('div', {
        ref,
        role: 'menu',
        'aria-label': rootLabel,
        'data-arbiter-cm': '1',
        onContextMenu: (e) => e.preventDefault(),
        style: {
          position: 'fixed',
          left: pos.left, top: pos.top,
          minWidth: 232, maxWidth: 336,
          background: 'var(--color-bg-card)',
          border: `1px solid var(--color-border-medium)`,
          borderRadius: 10,
          boxShadow: '0 16px 40px rgba(10,22,40,0.22), 0 2px 8px rgba(10,22,40,0.08), 0 0 0 1px var(--color-border-light) inset',
          padding: '4px 0',
          zIndex: 1300,
          fontFamily: T.font.family,
          fontSize: 12.5,
          letterSpacing: '-0.005em',
          userSelect: 'none',
          backdropFilter: 'saturate(180%) blur(8px)',
          WebkitBackdropFilter: 'saturate(180%) blur(8px)',
          animation: 'arbiter-cm-in 140ms cubic-bezier(0.2,0,0,1)',
        },
      },
        items.map((it, i) => {
          if (it.separator) return R.createElement('div', {
            key: it.__id, role: 'separator',
            style: {
              height: 1,
              background: 'linear-gradient(90deg, transparent 0, var(--color-border-light) 12%, var(--color-border-light) 88%, transparent 100%)',
              margin: '4px 6px',
            },
          });
          if (it.heading) return R.createElement('div', {
            key: it.__id,
            className: 'arb-caps',
            style: {
              fontSize: 9.5,
              color: 'var(--color-text-tertiary)',
              padding: '8px 14px 4px',
              whiteSpace: 'nowrap',
              overflow: 'hidden',
              textOverflow: 'ellipsis',
            },
          }, it.heading);
          const isFocus = focusIdx === i;
          const isSubOpen = openSubIdx === i;
          const hasSub = Array.isArray(it.submenu) && it.submenu.length > 0;
          const color = it.danger
            ? 'var(--color-status-critical)'
            : it.disabled ? 'var(--color-text-tertiary)' : 'var(--color-text-primary)';
          return R.createElement('div', {
            key: it.__id,
            role: 'menuitem',
            'aria-disabled': !!it.disabled,
            'aria-haspopup': hasSub ? 'menu' : undefined,
            'aria-expanded': hasSub ? isSubOpen : undefined,
            tabIndex: -1,
            'data-mi-idx': i,
            onMouseEnter: () => { if (!it.disabled) setFocusIdx(i); if (hasSub) setOpenSubIdx(i); else setOpenSubIdx(null); },
            onMouseLeave: (e) => {
              // Keep submenu open while hovering into it; no-op here
            },
            onClick: (e) => {
              e.stopPropagation();
              if (it.disabled) return;
              activate(it, i);
            },
            ref: (el) => { itemRefs.current[i] = el; },
            style: {
              display: 'flex', alignItems: 'center', gap: 10,
              padding: '7px 14px',
              margin: '0 4px',
              borderRadius: 6,
              cursor: it.disabled ? 'not-allowed' : 'default',
              opacity: it.disabled ? 0.45 : 1,
              background: isFocus && !it.disabled ? 'var(--color-bg-hover)' : 'transparent',
              color,
              transition: 'background 80ms var(--easing-standard, ease), color 80ms',
              lineHeight: 1.3,
            },
          },
            R.createElement('span', {
              style: {
                width: 14, display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                color: it.danger ? 'var(--color-status-critical)' : 'var(--color-text-tertiary)',
                fontSize: 13, flexShrink: 0, opacity: 0.85,
              }, 'aria-hidden': 'true',
            }, it.icon ? (typeof it.icon === 'string' ? it.icon : it.icon) : null),
            R.createElement('span', { style: { flex: 1, minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', fontWeight: 450 } }, it.label),
            it.kbd && R.createElement('kbd', { className: 'arb-kbd', style: { flexShrink: 0 } }, it.kbd),
            it.hint && !hasSub && R.createElement('span', {
              className: 'arb-num',
              style: { fontSize: 10.5, color: 'var(--color-text-tertiary)', flexShrink: 0, maxWidth: 140, overflow: 'hidden', textOverflow: 'ellipsis' },
            }, it.hint),
            hasSub && R.createElement('span', { 'aria-hidden': 'true',
              style: { color: 'var(--color-text-tertiary)', fontSize: 10, marginLeft: 'auto', opacity: 0.7 } }, '›'),
          );
        })
      ),
      // Submenu
      openSubIdx != null && items[openSubIdx]?.submenu?.length
        ? R.createElement(Menu, {
            key: `sub-${openSubIdx}`,
            x: 0, y: 0,
            parentRect: subRect,
            items: normalize(items[openSubIdx].submenu),
            onClose,
            rootLabel: 'Submenu',
          })
        : null
    );
  }

  // ── Inject keyframes once ───────────────────────────────────────────────
  if (typeof document !== 'undefined' && !document.getElementById('arbiter-cm-keyframes')) {
    const s = document.createElement('style');
    s.id = 'arbiter-cm-keyframes';
    s.textContent = `
      @keyframes arbiter-cm-in {
        from { opacity: 0; transform: translateY(-3px) scale(0.985); }
        to   { opacity: 1; transform: translateY(0)    scale(1); }
      }
    `;
    document.head.appendChild(s);
  }

  // ── Public API ──────────────────────────────────────────────────────────
  window.Arbiter = Object.assign(window.Arbiter || {}, {
    ContextMenu: { open: openMenu, close: () => closeFn && closeFn() },
    ContextMenuHost,
  });
})();
