/**
 * Global keyboard routing (spec §01D, docs/architecture/04 → "Global Events").
 *
 * One module owns the document-level `keydown` listener and dispatches the app's
 * shortcuts. Centralizing it keeps ordering explicit and gives one place to
 * reason about priority — components never attach their own `window` keydown
 * handlers (see the "Don't" list in docs/architecture/04 §2).
 *
 * The shortcut map (spec §01D), platform-aware (Cmd on Mac, Ctrl elsewhere):
 *   Cmd/Ctrl + Shift + N  → new snippet
 *   Cmd/Ctrl + K          → toggle the Datasets manager
 *   Cmd/Ctrl + S          → publish the active snippet's draft
 *   Cmd/Ctrl + ,          → open the editor settings popover
 *   Escape                → close the active modal
 *
 * Interactive-context gating (the `isInInteractiveContext` seam):
 *
 * - **Escape** is checked *before* the gate — it must dismiss a modal even while
 *   focus is in the editor or an input.
 * - **Cmd/Ctrl+S** is also checked before the gate. It is the canonical "save"
 *   shortcut and the spec mandates it override the browser default; publishing is
 *   something the user does *while editing the draft in Monaco*, so gating it
 *   behind "not typing" would defeat its purpose. (Deliberate divergence from the
 *   arch sketch, which placed Cmd+S after the gate.)
 * - **N / K / ,** are checked *after* the gate, so they never steal a keystroke
 *   from the editor or an input (e.g. Monaco's own Cmd+K chord).
 */

import { closeModal, toggleDatasets } from '../modals/ModalCoordinator';
import { useAppStore } from '../stores/AppStore';
import { useSnippetStore } from '../stores/SnippetStore';
import { openPopover } from '../stores/PopoverStore';
import { publishActiveSnippet } from '../services/snippet-actions';
import { isInInteractiveContext } from './focus-utils';

let started = false;

const isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform);

function modifier(e: KeyboardEvent): boolean {
  return isMac ? e.metaKey : e.ctrlKey;
}

/** Returns true if it consumed the Escape (caller should `preventDefault`). */
function handleEscape(): boolean {
  // Route through the coordinator so the unsaved-change discard prompt runs and
  // the URL is cleared. NEVER setActiveModal(null) here — that would silently
  // drop in-progress dataset / chart-builder edits. Escape only acts when a
  // modal is open; with no modal it does nothing (spec §01D).
  if (useAppStore.getState().activeModal) {
    void closeModal();
    return true;
  }
  return false;
}

function onKeyDown(e: KeyboardEvent): void {
  // Escape: highest priority, runs even while the editor / an input has focus.
  if (e.key === 'Escape') {
    if (handleEscape()) e.preventDefault();
    return;
  }

  if (!modifier(e)) return;
  const key = e.key.toLowerCase();

  // Cmd/Ctrl+S: publish. Handled before the interactive-context gate so it works
  // while editing the draft, and overrides the browser "save page" dialog.
  if (key === 's' && !e.shiftKey) {
    e.preventDefault();
    publishActiveSnippet();
    return;
  }

  // Everything below must not fire while the user is typing.
  if (isInInteractiveContext()) return;

  // Cmd/Ctrl+Shift+N: new snippet (selection drives the URL via the routing
  // subscription, so no explicit navigate call is needed).
  if (key === 'n' && e.shiftKey) {
    e.preventDefault();
    useSnippetStore.getState().createSnippet();
    return;
  }
  // Cmd/Ctrl+K: toggle the Datasets manager (coordinator owns open/close + URL).
  if (key === 'k' && !e.shiftKey) {
    e.preventDefault();
    toggleDatasets();
    return;
  }
  // Cmd/Ctrl+,: open the editor settings popover. Settings are distributed to
  // per-pane disclosure popovers, not a modal (spec §07); the editor cluster is
  // the primary one (the arch sketch's openModal('settings') predates that).
  if (e.key === ',') {
    e.preventDefault();
    openPopover('editor-settings');
    return;
  }
}

/** Bind the global keyboard listener. Idempotent; pair with `stopEventRouter`. */
export function startEventRouter(): void {
  if (started) return;
  started = true;
  window.addEventListener('keydown', onKeyDown);
}

/** Unbind the global keyboard listener (teardown for tests). */
export function stopEventRouter(): void {
  window.removeEventListener('keydown', onKeyDown);
  started = false;
}
