import { describe, expect, it, vi } from 'vitest';
import { createKeyRouter, type KeyEventLike } from './hotkeys';

/** A KeyboardEvent stand-in — the router only reads these fields, by design. */
function ev(key: string, extra: Partial<KeyEventLike> = {}): KeyEventLike {
  return { key, preventDefault: vi.fn(), ...extra };
}

describe('createKeyRouter', () => {
  it('routes a bound key to its handler', () => {
    const router = createKeyRouter();
    const hit = vi.fn();
    router.bind('r', hit);
    expect(router.handle(ev('r'))).toBe(true);
    expect(hit).toHaveBeenCalledTimes(1);
  });

  it('matches case-insensitively, so shift-R still rotates', () => {
    const router = createKeyRouter();
    const hit = vi.fn();
    router.bind('r', hit);
    router.handle(ev('R'));
    expect(hit).toHaveBeenCalled();
  });

  it('ignores unbound keys', () => {
    const router = createKeyRouter();
    expect(router.handle(ev('q'))).toBe(false);
  });

  it('swallows space so the page does not scroll under the factory', () => {
    const router = createKeyRouter();
    router.bind(' ', () => {});
    const e = ev(' ');
    router.handle(e);
    expect(e.preventDefault).toHaveBeenCalled();
  });

  it('does not preventDefault on keys the browser should keep handling', () => {
    const router = createKeyRouter();
    router.bind('r', () => {});
    const e = ev('r');
    router.handle(e);
    expect(e.preventDefault).not.toHaveBeenCalled();
  });

  it('leaves browser shortcuts alone (cmd/ctrl/alt held)', () => {
    const router = createKeyRouter();
    const hit = vi.fn();
    router.bind('r', hit);
    for (const mod of ['metaKey', 'ctrlKey', 'altKey'] as const) {
      expect(router.handle(ev('r', { [mod]: true }))).toBe(false);
    }
    expect(hit).not.toHaveBeenCalled();
  });

  it('lets a later bind replace an earlier one for the same key', () => {
    const router = createKeyRouter();
    const first = vi.fn();
    const second = vi.fn();
    router.bind('1', first);
    router.bind('1', second);
    router.handle(ev('1'));
    expect(first).not.toHaveBeenCalled();
    expect(second).toHaveBeenCalled();
  });

  it('binds the paging keys independently', () => {
    const router = createKeyRouter();
    const prev = vi.fn();
    const next = vi.fn();
    router.bind('[', prev);
    router.bind(']', next);
    router.handle(ev(']'));
    expect(next).toHaveBeenCalledTimes(1);
    expect(prev).not.toHaveBeenCalled();
  });
});
