// GUI keyboard shortcuts. Pure front-end concern (the daemon/CLI don't use these),
// so bindings live in localStorage and are user-rebindable from Settings.
//
// All defaults use ⌘ so they never collide with characters typed into a terminal
// (xterm receives plain keys; ⌘-combos are app shortcuts). The central handler in
// App.tsx requires at least one modifier before it will swallow a key.

export type HotkeyId =
  | "newWorkspace"
  | "openSettings"
  | "toggleSidebar"
  | "toggleEvents"
  | "splitRight"
  | "splitDown"
  | "closePanel"
  | "focusNext"
  | "focusPrev"
  | "zoomToggle"
  | "search";

export interface Binding {
  meta?: boolean;
  ctrl?: boolean;
  alt?: boolean;
  shift?: boolean;
  key: string; // normalized via normKey()
}

export interface HotkeyDef {
  id: HotkeyId;
  label: string;
  group: "Workspace" | "Panel";
  def: Binding;
}

export const HOTKEYS: HotkeyDef[] = [
  { id: "newWorkspace", label: "New workspace",       group: "Workspace", def: { meta: true, key: "n" } },
  { id: "openSettings", label: "Open settings",       group: "Workspace", def: { meta: true, key: "," } },
  { id: "toggleSidebar", label: "Toggle sidebar",     group: "Workspace", def: { meta: true, key: "b" } },
  { id: "toggleEvents", label: "Toggle event center", group: "Workspace", def: { meta: true, key: "e" } },
  { id: "splitRight",  label: "Split right",          group: "Panel",     def: { meta: true, key: "d" } },
  { id: "splitDown",   label: "Split down",           group: "Panel",     def: { meta: true, shift: true, key: "d" } },
  { id: "closePanel",  label: "Close panel",          group: "Panel",     def: { meta: true, key: "w" } },
  { id: "focusNext",   label: "Focus next panel",     group: "Panel",     def: { meta: true, key: "]" } },
  { id: "focusPrev",   label: "Focus previous panel", group: "Panel",     def: { meta: true, key: "[" } },
  { id: "zoomToggle",  label: "Toggle zoom",          group: "Panel",     def: { meta: true, key: "Enter" } },
  { id: "search",      label: "Search scrollback",    group: "Panel",     def: { meta: true, key: "f" } },
];

const STORE_KEY = "spacesh.hotkeys";

export type Bindings = Record<HotkeyId, Binding>;

export function defaultBindings(): Bindings {
  const out = {} as Bindings;
  for (const h of HOTKEYS) out[h.id] = h.def;
  return out;
}

export function loadBindings(): Bindings {
  const out = defaultBindings();
  try {
    const raw = localStorage.getItem(STORE_KEY);
    if (raw) {
      const o = JSON.parse(raw) as Partial<Bindings>;
      for (const h of HOTKEYS) if (o[h.id]) out[h.id] = o[h.id]!;
    }
  } catch { /* ignore */ }
  return out;
}

export function saveBindings(b: Bindings): void {
  try { localStorage.setItem(STORE_KEY, JSON.stringify(b)); } catch { /* ignore */ }
}

/** Stable key token: single chars lowercased, named keys kept verbatim. */
export function normKey(k: string): string {
  if (k === " ") return "Space";
  return k.length === 1 ? k.toLowerCase() : k;
}

const MOD_KEYS = new Set(["Meta", "Control", "Alt", "Shift"]);

/** Capture a binding from a keydown event (null while only modifiers are held). */
export function eventBinding(e: KeyboardEvent): Binding | null {
  if (MOD_KEYS.has(e.key)) return null;
  return { meta: e.metaKey, ctrl: e.ctrlKey, alt: e.altKey, shift: e.shiftKey, key: normKey(e.key) };
}

export function matches(b: Binding, e: KeyboardEvent): boolean {
  return (
    !!b.meta === e.metaKey &&
    !!b.ctrl === e.ctrlKey &&
    !!b.alt === e.altKey &&
    !!b.shift === e.shiftKey &&
    b.key === normKey(e.key)
  );
}

export function hasModifier(b: Binding): boolean {
  return !!(b.meta || b.ctrl || b.alt);
}

/** Human-readable chord, e.g. "⌘⇧D". */
export function formatBinding(b: Binding): string {
  const parts: string[] = [];
  if (b.ctrl) parts.push("⌃");
  if (b.alt) parts.push("⌥");
  if (b.shift) parts.push("⇧");
  if (b.meta) parts.push("⌘");
  const key =
    b.key === "Enter" ? "⏎" :
    b.key === "Space" ? "␣" :
    b.key === "ArrowUp" ? "↑" : b.key === "ArrowDown" ? "↓" :
    b.key === "ArrowLeft" ? "←" : b.key === "ArrowRight" ? "→" :
    b.key.length === 1 ? b.key.toUpperCase() : b.key;
  return parts.join("") + key;
}
