// Global shortcut bindings. Routes all calls through the Electron
// preload bridge (`window.electronAPI`). The exported surface is stable
// across the Tauri → Electron migration so no calling code changes.
//
// `isTauriRuntime()` is kept as a named export for back-compat — many
// callers import it. The implementation now checks for the Electron
// preload marker instead. See `isNativeRuntime` for a forward-looking
// name.

type Unsubscribe = () => void;

export function isNativeRuntime(): boolean {
  return (
    typeof window !== 'undefined' &&
    typeof window.electronAPI !== 'undefined' &&
    window.electronAPI.platform === 'electron-chatapp-v1'
  );
}

// Back-compat alias. New code should prefer `isNativeRuntime`.
export const isTauriRuntime = isNativeRuntime;

// Converts a KeyboardEvent.code (what PTT settings store) into an
// Electron accelerator token. Electron's accelerator syntax is close
// enough to the DOM `.code` naming for most keys; single-key strips the
// "Key"/"Digit" prefix that DOM adds.
export function codeToShortcut(code: string): string {
  if (code.startsWith('Key')) return code.slice(3);
  if (code.startsWith('Digit')) return code.slice(5);
  return code;
}

// ---- Single-shortcut event router ----------------------------------------
//
// Electron's preload delivers all fired/released events on one channel;
// we fan out to per-id callbacks here so each caller can register
// independently without re-subscribing at the IPC layer.

interface EventEntry {
  onPress: () => void;
  onRelease?: () => void;
}

const handlers = new Map<string, EventEntry>();
let firedUnsub: Unsubscribe | null = null;
let releasedUnsub: Unsubscribe | null = null;

function ensureSubscribed(): void {
  if (!firedUnsub) {
    firedUnsub = window.electronAPI.onShortcutFired((evt) => {
      const entry = handlers.get(evt.id);
      entry?.onPress();
    });
  }
  if (!releasedUnsub) {
    releasedUnsub = window.electronAPI.onShortcutReleased((evt) => {
      const entry = handlers.get(evt.id);
      entry?.onRelease?.();
    });
  }
}

async function registerShortcut(
  id: string,
  accelerator: string,
  kind: 'press' | 'ptt',
  onPress: () => void,
  onRelease?: () => void,
): Promise<boolean> {
  if (!isNativeRuntime()) return false;
  ensureSubscribed();
  if (handlers.has(id)) {
    try {
      await window.electronAPI.unregisterShortcut(id);
    } catch {
      /* ignore */
    }
    handlers.delete(id);
  }
  try {
    const ok = await window.electronAPI.registerShortcut({ id, accelerator, kind });
    if (!ok) return false;
    handlers.set(id, onRelease ? { onPress, onRelease } : { onPress });
    return true;
  } catch (err: unknown) {
    console.warn('registerShortcut failed', { id, accelerator, err });
    return false;
  }
}

async function unregisterShortcutById(id: string): Promise<void> {
  if (!isNativeRuntime()) {
    handlers.delete(id);
    return;
  }
  handlers.delete(id);
  try {
    await window.electronAPI.unregisterShortcut(id);
  } catch (err: unknown) {
    console.warn('unregisterShortcut failed', { id, err });
  }
}

// ---- PTT (press + release) ------------------------------------------------
//
// PTT release semantics under Electron's globalShortcut are simulated —
// main auto-fires SHORTCUT_EVT_RELEASED 200ms after each press. See the
// main-side shortcuts module for the limitation.

export async function registerPttShortcut(
  code: string,
  onPress: () => void,
  onRelease: () => void,
): Promise<boolean> {
  return registerShortcut('ptt', codeToShortcut(code), 'ptt', onPress, onRelease);
}

export async function unregisterPttShortcut(_code: string): Promise<void> {
  await unregisterShortcutById('ptt');
}

// ---- Press-only (toggles like mute/deafen) -------------------------------

export async function registerGlobalShortcutPress(
  shortcut: string,
  onPress: () => void,
): Promise<boolean> {
  return registerShortcut(`press:${shortcut}`, shortcut, 'press', onPress);
}

export async function unregisterGlobalShortcut(shortcut: string): Promise<void> {
  await unregisterShortcutById(`press:${shortcut}`);
}

// ---- Soundboard shortcuts ------------------------------------------------

interface SoundShortcutRegistration {
  shortcut: string;
  onPress: () => void;
}

const soundRegistry = new Map<string, SoundShortcutRegistration>();

function soundId(id: string): string {
  return `sound:${id}`;
}

export async function registerSoundShortcut(
  id: string,
  code: string,
  onPress: () => void,
): Promise<boolean> {
  if (!isNativeRuntime()) return false;
  const shortcut = codeToShortcut(code);
  await unregisterSoundShortcut(id);
  const ok = await registerShortcut(soundId(id), shortcut, 'press', onPress);
  if (ok) soundRegistry.set(id, { shortcut, onPress });
  return ok;
}

export async function unregisterSoundShortcut(id: string): Promise<void> {
  const reg = soundRegistry.get(id);
  if (!reg) return;
  soundRegistry.delete(id);
  if (!isNativeRuntime()) return;
  await unregisterShortcutById(soundId(id));
}

export async function unregisterAllSoundShortcuts(): Promise<void> {
  const ids = Array.from(soundRegistry.keys());
  await Promise.all(ids.map((id) => unregisterSoundShortcut(id)));
}

export function soundShortcutOwnerFor(code: string): string | null {
  const shortcut = codeToShortcut(code);
  for (const [id, reg] of soundRegistry) {
    if (reg.shortcut === shortcut) return id;
  }
  return null;
}
