/* global React */
// Keyboard shortcuts overlay — hidden from the UI (no visible button).
// `?` opens the help menu, leader keys (F / R / G) start a chord, single
// utility keys (/, ⌘K, ,) trigger actions, `Esc` closes whatever's open.
// Power-user only; not surfaced in the chrome on purpose.
//
// Leader-key model — maps to the VF—X.Y / VR—X.Y / VG—X.Y page eyebrows.
//
//   F        → Films home (VF—1.0)            R       → Rentals home
//   F1       → Films home (same)              R1      → Rentals home
//   F11      → home + scroll to Selected work R11     → landing + #shelf
//   F12      → home + scroll to The Studio    R12     → landing + #featured
//   F13      → home + scroll to Reviews       R13     → landing + #reviews
//   F2       → Services                       R14     → landing + #faqs
//   F21      → Services + scroll to Process   R2      → Equipment
//   F3       → About                          R3      → About (rentals)
//   F31      → About + scroll to Principles   R4      → Open Account
//   F32      → About + scroll to Team         R5      → Careers (hidden)
//   F4       → Contact
//   F5       → Crew onboard (hidden)
//   F6       → Privacy (hidden)
//
//   G        → Games landing (VG—1.0)         G2      → Clapperboard Chaos
//   G1       → Games landing (same)           G3      → Focus Fever
//
// Chord timing:
//   Leader pressed, no digit within 800ms → fire side-home
//   First digit pressed, no second digit within 350ms → fire 1-digit target
//   Second digit pressed → fire 2-digit target immediately
//
// If a 2-digit chord doesn't exist in the map, the handler falls back to
// the 1-digit destination (e.g. F19 isn't defined → goes to Films home).
//
// Games is a separate static site at /games/, not part of the SPA, so the
// G-leader does a full page navigation (window.location.href) rather than
// pushState.
const { useState: useShortcutState, useEffect: useShortcutEffect, useRef: useShortcutRef } = React;

const LEADER_MAP = {
  f: {
    '':   '/',
    '1':  '/',
    '11': '/#work',
    '12': '/#studio',
    '13': '/#reviews',
    '2':  '/services',
    '21': '/services#process',
    '3':  '/about',
    '31': '/about#principles',
    '32': '/about#team',
    '4':  '/contact',
    '5':  '/crew/onboard',
    '6':  '/privacy',
  },
  r: {
    '':   '/rentals',
    '1':  '/rentals',
    '11': '/rentals#shelf',
    '12': '/rentals#featured',
    '13': '/rentals#reviews',
    '14': '/rentals#faqs',
    '2':  '/rentals/equipment',
    '3':  '/rentals/about',
    '4':  '/rentals/open-account',
    '5':  '/rentals/careers',
    // R6 Reference is token-based — no useful shortcut, intentionally omitted.
  },
  g: {
    '':   '/games/',
    '1':  '/games/',
    '2':  '/games/clapperboardchaos/',
    '3':  '/games/focusfever/',
  },
};

const LEADER_TIMEOUT_MS = 800;       // After leader, wait for first digit
const SECOND_DIGIT_TIMEOUT_MS = 350; // After first digit, wait for second

// Leaders that route OUT of the SPA — these use full-page navigation
// rather than pushState because the target is a separate static site.
const EXTERNAL_LEADERS = new Set(['g']);

const SHORTCUT_GROUPS = [
  { title: 'Films', items: [
    { keys: ['F'],            label: 'Home' },
    { keys: ['F', '1', '1'],  chord: true, label: 'Selected work' },
    { keys: ['F', '1', '2'],  chord: true, label: 'The Studio' },
    { keys: ['F', '1', '3'],  chord: true, label: 'What clients say' },
    { keys: ['F', '2'],       chord: true, label: 'Services' },
    { keys: ['F', '2', '1'],  chord: true, label: 'How we work' },
    { keys: ['F', '3'],       chord: true, label: 'About' },
    { keys: ['F', '3', '1'],  chord: true, label: 'Studio principles' },
    { keys: ['F', '3', '2'],  chord: true, label: 'The team' },
    { keys: ['F', '4'],       chord: true, label: 'Contact' },
    { keys: ['F', '5'],       chord: true, label: 'Crew' },
    { keys: ['F', '6'],       chord: true, label: 'Privacy' },
  ]},
  { title: 'Rentals', items: [
    { keys: ['R'],            label: 'Rentals home' },
    { keys: ['R', '1', '1'],  chord: true, label: 'The shelf' },
    { keys: ['R', '1', '2'],  chord: true, label: 'Featured kits' },
    { keys: ['R', '1', '3'],  chord: true, label: 'Reviews' },
    { keys: ['R', '1', '4'],  chord: true, label: 'FAQ' },
    { keys: ['R', '2'],       chord: true, label: 'Equipment' },
    { keys: ['R', '3'],       chord: true, label: 'About (rentals)' },
    { keys: ['R', '4'],       chord: true, label: 'Open Account' },
    { keys: ['R', '5'],       chord: true, label: 'Careers' },
  ]},
  { title: 'Games', items: [
    { keys: ['G'],            label: 'Games landing' },
    { keys: ['G', '2'],       chord: true, label: 'Clapperboard Chaos' },
    { keys: ['G', '3'],       chord: true, label: 'Focus Fever' },
  ]},
  { title: 'Actions', items: [
    { keys: ['/'],              label: 'Focus search' },
    { keys: ['⌘ K', 'Ctrl K'],  label: 'Focus search' },
    { keys: [','],              label: 'Toggle cart' },
  ]},
  { title: 'Help', items: [
    { keys: ['?'],   label: 'Show this menu' },
    { keys: ['Esc'], label: 'Close menu / drawer / modal' },
  ]},
];

// True when the user is typing into a field — we should not steal
// single-letter keystrokes from form inputs, search boxes, etc.
function isTypingTarget(target) {
  if (!target) return false;
  const tag = (target.tagName || '').toLowerCase();
  if (tag === 'input' || tag === 'textarea' || tag === 'select') return true;
  if (target.isContentEditable) return true;
  return false;
}

function ShortcutsMenu({ open, onClose }) {
  return (
    <div
      className={`vf-shortcuts-overlay ${open ? 'is-open' : ''}`}
      role="dialog"
      aria-modal="true"
      aria-label="Keyboard shortcuts"
      aria-hidden={!open}
      onClick={onClose}>
      <div className="vf-shortcuts-panel" onClick={(e) => e.stopPropagation()}>
        <header className="vf-shortcuts-head">
          <div className="vf-shortcuts-eyebrow">Keyboard shortcuts</div>
          <button
            type="button"
            className="vf-shortcuts-close"
            onClick={onClose}
            aria-label="Close shortcuts">×</button>
        </header>
        <div className="vf-shortcuts-grid">
          {SHORTCUT_GROUPS.map((grp) => (
            <section key={grp.title} className="vf-shortcuts-group">
              <h3>{grp.title}</h3>
              <dl>
                {grp.items.map((it) => (
                  <React.Fragment key={it.label + it.keys.join()}>
                    <dt>
                      {it.keys.map((k, i) => (
                        <React.Fragment key={i}>
                          {i > 0 && !it.chord && <span className="vf-shortcuts-or"> or </span>}
                          <kbd>{k}</kbd>
                        </React.Fragment>))}
                    </dt>
                    <dd>{it.label}</dd>
                  </React.Fragment>))}
              </dl>
            </section>))}
        </div>
        <footer className="vf-shortcuts-foot">
          Press <kbd>Esc</kbd> to close · Hidden help (<kbd>?</kbd> from anywhere)
        </footer>
      </div>
    </div>);
}

function KeyboardShortcuts({ goto }) {
  const [open, setOpen] = useShortcutState(false);
  const openRef = useShortcutRef(open);
  openRef.current = open;

  // Chord state — leader letter + buffered digits + pending timeout. Refs
  // because the keydown handler is bound once and mustn't see stale state.
  const leaderRef = useShortcutRef(null);
  const digitsRef = useShortcutRef('');
  const timerRef = useShortcutRef(null);

  const clearLeader = () => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
      timerRef.current = null;
    }
    leaderRef.current = null;
    digitsRef.current = '';
  };

  // Navigate to a target. SPA leaders (F, R) use the in-app router and
  // smooth-scroll to any hash anchor. External leaders (G) do a full
  // page load since /games/ isn't part of the React app.
  const navigateTo = (target, external) => {
    if (external) {
      window.location.href = target;
      return;
    }
    const hashIdx = target.indexOf('#');
    const path = hashIdx === -1 ? target : target.slice(0, hashIdx);
    const hash = hashIdx === -1 ? '' : target.slice(hashIdx + 1);
    const samePath = window.location.pathname === path;

    const scrollToHash = () => {
      if (!hash) return;
      const el = document.getElementById(hash);
      if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
    };

    if (samePath) {
      if (hash) scrollToHash();
      else window.scrollTo({ top: 0, behavior: 'smooth' });
    } else {
      goto(path);
      if (hash) setTimeout(scrollToHash, 80);
    }
  };

  // Resolve a chord against the leader map, with fall-back: F19 (no such
  // section) drops the trailing digit and goes to F1.
  const resolveChord = (leader, digits) => {
    const map = LEADER_MAP[leader];
    if (!map) return null;
    if (digits in map) return map[digits];
    if (digits.length > 1 && digits[0] in map) return map[digits[0]];
    return map[''] || null;
  };

  const fireChord = (leader, digits) => {
    const target = resolveChord(leader, digits);
    clearLeader();
    if (target) navigateTo(target, EXTERNAL_LEADERS.has(leader));
  };

  const startLeader = (side) => {
    // Same leader pressed twice → commit to side-home immediately.
    if (leaderRef.current === side && digitsRef.current === '') {
      fireChord(side, '');
      return;
    }
    // Switching leader mid-window → cancel old, start new.
    if (timerRef.current) clearTimeout(timerRef.current);
    leaderRef.current = side;
    digitsRef.current = '';
    timerRef.current = setTimeout(() => {
      timerRef.current = null;
      if (leaderRef.current === side) fireChord(side, '');
    }, LEADER_TIMEOUT_MS);
  };

  const addDigit = (digit) => {
    const leader = leaderRef.current;
    if (!leader) return;
    if (timerRef.current) clearTimeout(timerRef.current);

    const next = digitsRef.current + digit;
    digitsRef.current = next;

    if (next.length >= 2) {
      // Second digit completes the chord — fire immediately.
      fireChord(leader, next);
      return;
    }
    // First digit: wait briefly for a possible second digit.
    timerRef.current = setTimeout(() => {
      timerRef.current = null;
      if (leaderRef.current === leader && digitsRef.current === next) {
        fireChord(leader, next);
      }
    }, SECOND_DIGIT_TIMEOUT_MS);
  };

  // DOM-driven actions for the rentals-only widgets. Silent no-op on
  // Films pages where those elements don't exist.
  const focusSearch = () => {
    const input = document.querySelector('.rfs-bar input');
    if (input) input.focus();
  };
  const toggleCart = () => {
    const btn = document.querySelector('.rfs-cart');
    if (btn) btn.click();
  };

  // Esc closes whatever is open: shortcuts menu → leader window →
  // cart drawer → catalog drawer → video modal.
  const closeAll = () => {
    if (openRef.current) { setOpen(false); return; }
    if (leaderRef.current) { clearLeader(); return; }
    const cartClose = document.querySelector('.rcart-drawer .rcart-close, .rcart-drawer [aria-label*="lose"]');
    if (cartClose && cartClose.offsetParent !== null) { cartClose.click(); return; }
    const catalogClose = document.querySelector('.rcat-drawer-close, .rcat-drawer [aria-label*="lose"]');
    if (catalogClose && catalogClose.offsetParent !== null) { catalogClose.click(); return; }
    const videoClose = document.querySelector('.vf-modal-close, .video-modal [aria-label*="lose"]');
    if (videoClose && videoClose.offsetParent !== null) { videoClose.click(); return; }
  };

  useShortcutEffect(() => {
    const onKey = (e) => {
      // `?` always opens the menu (Shift+/ on UK and US layouts).
      if (e.key === '?') {
        e.preventDefault();
        clearLeader();
        setOpen((v) => !v);
        return;
      }
      // Esc closes whatever's open (including a pending leader).
      if (e.key === 'Escape') {
        closeAll();
        return;
      }
      // Cmd/Ctrl + K focuses the search.
      if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')) {
        e.preventDefault();
        focusSearch();
        return;
      }
      // Never steal modifier-key combos (Cmd+R reload, Cmd+S save…).
      if (e.metaKey || e.ctrlKey || e.altKey) return;
      // Don't steal keystrokes from form inputs.
      if (isTypingTarget(e.target)) return;
      // If the help menu is open, only the close handlers above run.
      if (openRef.current) return;

      const key = e.key.toLowerCase();

      // Active leader — consume digits, cancel on other keys.
      if (leaderRef.current) {
        if (/^[0-9]$/.test(e.key)) {
          e.preventDefault();
          addDigit(e.key);
          return;
        }
        if (key !== 'f' && key !== 'r' && key !== 'g') {
          // Non-leader, non-digit while leader is active → fire whatever
          // we have (page-only if any digits buffered, otherwise side-home)
          // and fall through so the key can still trigger its own action.
          fireChord(leaderRef.current, digitsRef.current);
        }
      }

      // Leader keys.
      if (key === 'f' || key === 'r' || key === 'g') {
        e.preventDefault();
        startLeader(key);
        return;
      }

      // Single-key utility actions.
      if (e.key === '/') {
        e.preventDefault();
        focusSearch();
        return;
      }
      if (e.key === ',') {
        e.preventDefault();
        toggleCart();
        return;
      }
    };
    window.addEventListener('keydown', onKey);
    return () => {
      window.removeEventListener('keydown', onKey);
      if (timerRef.current) clearTimeout(timerRef.current);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return <ShortcutsMenu open={open} onClose={() => setOpen(false)} />;
}

Object.assign(window, { KeyboardShortcuts });
