/* @mostly-tiny/cmdk — the ⌘K command palette (browser kit).
 *
 * A shared, framework-standard command menu: press ⌘K (mac) / Ctrl-K (win/linux)
 * to open a searchable overlay of the site's pages + actions, navigate with the
 * arrow keys, run with ↵, dismiss with Esc. Built once here, dropped into any
 * Mostly Tiny product's header the same way the Footer is.
 *
 * Delivery mirrors forge-kit.jsx / auth-kit.js: served verbatim, compiled in the
 * browser by @babel/standalone, no bundler. It reads window.React (UMD) and
 * window.ForgeCmdKFuzzy (cmdk-fuzzy.js, loaded before this file) and publishes:
 *
 *   window.ForgeCmdK = {
 *     Palette,        // <ForgeCmdK.Palette items open onOpenChange … />  — the overlay
 *     Trigger,        // <ForgeCmdK.Trigger onClick />  — the header search button + ⌘K hint
 *     isMac,          // () => boolean  (SSR-safe: false when navigator is absent)
 *     shortcutLabel,  // () => "⌘K" | "Ctrl K"
 *     fuzzy,          // the ForgeCmdKFuzzy core
 *   }
 *
 * Item shape (Raycast/cmdk-style):
 *   { id, title, subtitle?, group?, keywords?(string|string[]), icon?(node),
 *     shortcut?(string), href?, perform?(item)=>void, disabled? }
 * Selecting an item runs perform(item) if present, else navigates href.
 */
(function () {
  'use strict';
  if (typeof window === 'undefined' || !window.React) return; // SSR/no-DOM: nothing to mount
  const React = window.React;
  const ReactDOM = window.ReactDOM;
  const { useState, useEffect, useRef, useMemo, useCallback } = React;
  const fuzzy = window.ForgeCmdKFuzzy || { rank: (q, i) => i.map((item) => ({ item, positions: [] })), segments: (t) => [{ text: t, match: false }] };

  const isMac = () =>
    typeof navigator !== 'undefined' &&
    /mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent || '');
  const shortcutLabel = () => (isMac() ? '⌘K' : 'Ctrl K');

  // ── icons (inline, so the kit carries no asset dependency) ──
  const Svg = (props, ...children) =>
    React.createElement('svg', Object.assign({ width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }, props), ...children);
  const SearchIcon = () => Svg({ 'aria-hidden': true }, React.createElement('circle', { cx: 11, cy: 11, r: 8 }), React.createElement('path', { d: 'm21 21-4.3-4.3' }));
  const CornerReturn = () => Svg({ width: 13, height: 13, 'aria-hidden': true }, React.createElement('polyline', { points: '9 10 4 15 9 20' }), React.createElement('path', { d: 'M20 4v7a4 4 0 0 1-4 4H4' }));
  const DefaultItemIcon = () => Svg({ width: 15, height: 15, 'aria-hidden': true }, React.createElement('circle', { cx: 12, cy: 12, r: 9 }), React.createElement('path', { d: 'M12 8v8M8 12h8' }));

  // Render a label with the fuzzy-matched characters emphasised.
  function Highlight({ text, positions }) {
    if (!positions || !positions.length) return text;
    return fuzzy.segments(text, positions).map((seg, i) =>
      seg.match
        ? React.createElement('mark', { key: i, className: 'fcmdk-mark' }, seg.text)
        : React.createElement('span', { key: i }, seg.text)
    );
  }

  // Split a shortcut string like "⌘⇧P" / "G then H" into <kbd> chips.
  function Kbd({ combo }) {
    if (!combo) return null;
    const parts = String(combo).trim().split(/\s+/);
    return React.createElement('span', { className: 'fcmdk-kbds' },
      parts.map((p, i) => React.createElement('kbd', { key: i, className: 'fcmdk-kbd' }, p)));
  }

  /**
   * The header button. Shows a search glyph, a "Search" label (hidden on narrow
   * viewports via CSS) and the platform shortcut hint. Purely presentational —
   * the parent owns palette open state and passes onClick.
   */
  function Trigger({ onClick, label, className, title }) {
    // Defer platform detection to post-mount so the server render (navigator absent →
    // "Ctrl K") and the first client render agree — then upgrade to "⌘K" on a Mac.
    // Prevents a hydration mismatch on the prerendered header.
    const [combo, setCombo] = useState('Ctrl K');
    useEffect(() => { setCombo(shortcutLabel()); }, []);
    return React.createElement('button', {
      type: 'button',
      onClick,
      className: 'fcmdk-trigger' + (className ? ' ' + className : ''),
      'aria-label': title || 'Search (' + combo + ')',
      title: title || 'Search',
    },
      React.createElement('span', { className: 'fcmdk-trigger__icon' }, React.createElement(SearchIcon)),
      React.createElement('span', { className: 'fcmdk-trigger__label' }, label || 'Search'),
      React.createElement('span', { className: 'fcmdk-trigger__kbd', 'aria-hidden': true },
        combo.split(/\s+/).map((p, i) => React.createElement('kbd', { key: i }, p)))
    );
  }

  /**
   * The overlay. Controlled: parent holds `open` and gets `onOpenChange(false)`
   * on dismiss/select. Also binds the global ⌘K/Ctrl-K hotkey to request opening,
   * so a host only needs to render <Palette> once and wire a Trigger for the click.
   */
  function Palette(props) {
    const {
      items = [],
      open = false,
      onOpenChange,
      placeholder = 'Search pages and actions…',
      emptyLabel = 'No results',
      hotkey = true,
    } = props;

    const [query, setQuery] = useState('');
    const [active, setActive] = useState(0);
    const inputRef = useRef(null);
    const listRef = useRef(null);
    const activeRef = useRef(null);

    // Global hotkey: ⌘K / Ctrl-K toggles the palette from anywhere on the page.
    useEffect(() => {
      if (!hotkey || !onOpenChange) return undefined;
      const onKey = (e) => {
        const k = (e.key || '').toLowerCase();
        if (k === 'k' && (e.metaKey || e.ctrlKey) && !e.altKey && !e.shiftKey) {
          e.preventDefault();
          onOpenChange(!open);
        }
      };
      window.addEventListener('keydown', onKey);
      return () => window.removeEventListener('keydown', onKey);
    }, [hotkey, onOpenChange, open]);

    // Reset query + selection and focus the field each time it opens.
    useEffect(() => {
      if (open) {
        setQuery('');
        setActive(0);
        const t = setTimeout(() => { if (inputRef.current) inputRef.current.focus(); }, 0);
        return () => clearTimeout(t);
      }
      return undefined;
    }, [open]);

    // Lock body scroll while open.
    useEffect(() => {
      if (!open) return undefined;
      const prev = document.body.style.overflow;
      document.body.style.overflow = 'hidden';
      return () => { document.body.style.overflow = prev; };
    }, [open]);

    // Rank + (optionally) group the results.
    const ranked = useMemo(() => {
      // Field 0 is the display title (drives highlighting + ranks highest). Each keyword
      // phrase is its OWN short field, plus the group — NOT one long concatenation, and the
      // long subtitle/description is intentionally excluded from search. Short fields keep
      // fuzzy subsequence matching precise: a token only matches when it fits inside a single
      // phrase, so intent queries hit the right tool without spurious cross-phrase matches.
      const searchable = (it) => {
        const kw = Array.isArray(it.keywords) ? it.keywords : (it.keywords ? [it.keywords] : []);
        return [String(it.title || ''), ...kw.map(String), String(it.group || '')].filter(Boolean);
      };
      const r = fuzzy.rank(query, items.filter((it) => it && !it.hidden), searchable);
      return r;
    }, [query, items]);

    // Flat list (for keyboard nav) with group headers interleaved (for display).
    const rows = useMemo(() => {
      const out = [];
      let lastGroup = null;
      let flatIndex = 0;
      ranked.forEach((r) => {
        const g = r.item.group || '';
        if (g !== lastGroup) { out.push({ type: 'group', label: g }); lastGroup = g; }
        out.push({ type: 'item', data: r, index: flatIndex });
        flatIndex++;
      });
      return out;
    }, [ranked]);

    const total = ranked.length;
    useEffect(() => { if (active > total - 1) setActive(Math.max(0, total - 1)); }, [total, active]);

    // Keep the active row scrolled into view.
    useEffect(() => {
      if (activeRef.current && activeRef.current.scrollIntoView) {
        activeRef.current.scrollIntoView({ block: 'nearest' });
      }
    }, [active, open]);

    const close = useCallback(() => { if (onOpenChange) onOpenChange(false); }, [onOpenChange]);

    const runItem = useCallback((r) => {
      if (!r || !r.item || r.item.disabled) return;
      const it = r.item;
      close();
      if (typeof it.perform === 'function') it.perform(it);
      else if (it.href) {
        if (/^https?:\/\//i.test(it.href) && it.external) window.open(it.href, '_blank', 'noopener');
        else window.location.href = it.href;
      }
    }, [close]);

    const onKeyDown = useCallback((e) => {
      if (e.key === 'Escape') { e.preventDefault(); close(); return; }
      if (e.key === 'ArrowDown') { e.preventDefault(); setActive((a) => (total ? (a + 1) % total : 0)); return; }
      if (e.key === 'ArrowUp') { e.preventDefault(); setActive((a) => (total ? (a - 1 + total) % total : 0)); return; }
      if (e.key === 'Home') { e.preventDefault(); setActive(0); return; }
      if (e.key === 'End') { e.preventDefault(); setActive(Math.max(0, total - 1)); return; }
      if (e.key === 'Enter') { e.preventDefault(); runItem(ranked[active]); return; }
    }, [total, active, ranked, runItem, close]);

    if (!open || !ReactDOM || !ReactDOM.createPortal) return null;

    const listId = 'fcmdk-list';
    const optionId = (i) => 'fcmdk-opt-' + i;

    const overlay = React.createElement('div', {
      className: 'fcmdk-overlay',
      role: 'presentation',
      onMouseDown: (e) => { if (e.target === e.currentTarget) close(); },
    },
      React.createElement('div', {
        className: 'fcmdk-panel',
        role: 'dialog',
        'aria-modal': 'true',
        'aria-label': 'Command palette',
      },
        // ── search field ──
        React.createElement('div', { className: 'fcmdk-field' },
          React.createElement('span', { className: 'fcmdk-field__icon' }, React.createElement(SearchIcon)),
          React.createElement('input', {
            ref: inputRef,
            className: 'fcmdk-input',
            type: 'text',
            value: query,
            onChange: (e) => { setQuery(e.target.value); setActive(0); },
            onKeyDown,
            placeholder,
            role: 'combobox',
            'aria-expanded': true,
            'aria-controls': listId,
            'aria-activedescendant': total ? optionId(active) : undefined,
            'aria-autocomplete': 'list',
            autoComplete: 'off',
            autoCorrect: 'off',
            autoCapitalize: 'off',
            spellCheck: false,
          }),
          React.createElement('kbd', { className: 'fcmdk-esc', 'aria-hidden': true }, 'Esc')
        ),
        // ── results ──
        React.createElement('div', { className: 'fcmdk-list', id: listId, role: 'listbox', ref: listRef },
          total === 0
            ? React.createElement('div', { className: 'fcmdk-empty' }, emptyLabel)
            : rows.map((row, i) => {
              if (row.type === 'group') {
                return row.label
                  ? React.createElement('div', { key: 'g' + i, className: 'fcmdk-group', role: 'presentation' }, row.label)
                  : null;
              }
              const r = row.data;
              const it = r.item;
              const isActive = row.index === active;
              return React.createElement('div', {
                key: it.id || ('i' + row.index),
                id: optionId(row.index),
                role: 'option',
                'aria-selected': isActive,
                'aria-disabled': it.disabled || undefined,
                ref: isActive ? activeRef : null,
                className: 'fcmdk-item' + (isActive ? ' is-active' : '') + (it.disabled ? ' is-disabled' : ''),
                onMouseMove: () => setActive(row.index),
                onClick: () => runItem(r),
              },
                React.createElement('span', { className: 'fcmdk-item__icon' }, it.icon || React.createElement(DefaultItemIcon)),
                React.createElement('span', { className: 'fcmdk-item__text' },
                  React.createElement('span', { className: 'fcmdk-item__title' },
                    React.createElement(Highlight, { text: it.title || '', positions: r.positions })),
                  it.subtitle ? React.createElement('span', { className: 'fcmdk-item__sub' }, it.subtitle) : null
                ),
                it.badge ? React.createElement('span', { className: 'fcmdk-item__badge' }, it.badge) : null,
                it.shortcut ? React.createElement(Kbd, { combo: it.shortcut }) : null
              );
            })
        ),
        // ── footer hints ──
        React.createElement('div', { className: 'fcmdk-footer' },
          React.createElement('span', { className: 'fcmdk-hint' }, React.createElement('kbd', null, '↑'), React.createElement('kbd', null, '↓'), ' navigate'),
          React.createElement('span', { className: 'fcmdk-hint' }, React.createElement('kbd', null, React.createElement(CornerReturn)), ' open'),
          React.createElement('span', { className: 'fcmdk-hint' }, React.createElement('kbd', null, 'esc'), ' close')
        )
      )
    );

    return ReactDOM.createPortal(overlay, document.body);
  }

  window.ForgeCmdK = { Palette, Trigger, isMac, shortcutLabel, fuzzy };
})();
