// Binds soundboard entries to global shortcuts and keeps the registry in
// sync with storage mutations. Starting returns a teardown function that
// undoes every registration the binding made.
//
// Usage (in CallContext effect when call becomes connected):
//   const teardown = startSoundboardHotkeys((id) => void playSoundboard(id));
//   return teardown; // useEffect cleanup

import {
  isTauriRuntime,
  registerSoundShortcut,
  unregisterAllSoundShortcuts,
  unregisterSoundShortcut,
} from './globalShortcut';
import { getPttSettings } from './pttSettings';
import { listSounds, subscribeSoundboardChanges } from './soundboardStorage';

export type FirePress = (id: string) => void;

export function startSoundboardHotkeys(onPress: FirePress): () => void {
  if (!isTauriRuntime()) {
    // No-op on pure web preview — global shortcuts unsupported. Storage
    // change subscription would still fire, but there's nothing to sync.
    return () => undefined;
  }

  let active = true;
  // identity-keyed: sound id -> currently bound DOM code
  const bound = new Map<string, string>();

  const sync = async (): Promise<void> => {
    if (!active) return;
    let entries;
    try {
      entries = await listSounds();
    } catch (err: unknown) {
      console.warn('soundboardHotkeys list failed', err);
      return;
    }
    const pttKey = getPttSettings().key;

    const wantByCode = new Map<string, string>(); // code -> id (winner on conflict)
    for (const e of entries) {
      if (!e.hotkey) continue;
      // PTT wins over soundboard: don't hijack the talk key, skip silently.
      if (pttKey && e.hotkey === pttKey) continue;
      // First-writer-wins for dupes (stable because listSounds is sorted
      // deterministically). Settings UI should prevent this upstream.
      if (!wantByCode.has(e.hotkey)) wantByCode.set(e.hotkey, e.id);
    }

    const want = new Map<string, string>();
    for (const [code, id] of wantByCode) want.set(id, code);

    // Unregister bindings that disappeared or changed key.
    for (const [id, code] of bound) {
      const nextCode = want.get(id);
      if (nextCode !== code) {
        await unregisterSoundShortcut(id);
        bound.delete(id);
      }
    }

    // Register new / updated bindings.
    for (const [id, code] of want) {
      if (bound.get(id) === code) continue;
      const ok = await registerSoundShortcut(id, code, () => {
        if (!active) return;
        onPress(id);
      });
      if (ok) bound.set(id, code);
    }
  };

  const unsubscribe = subscribeSoundboardChanges(() => {
    void sync();
  });

  // Initial binding pass.
  void sync();

  return () => {
    active = false;
    unsubscribe();
    void unregisterAllSoundShortcuts();
    bound.clear();
  };
}
