// @vitest-environment happy-dom
import { describe, expect, it, vi } from 'vitest';
import { createHotkeys, isTypingTarget } from './hotkeys';

/** The half of hotkeys.ts that needs a real window: attach/detach and typing guards. */
describe('createHotkeys attach/detach', () => {
  it('routes real window keydown events once attached', () => {
    const keys = createHotkeys();
    const hit = vi.fn();
    keys.bind('r', hit);
    keys.attach();
    window.dispatchEvent(new KeyboardEvent('keydown', { key: 'r' }));
    expect(hit).toHaveBeenCalledTimes(1);
    keys.detach();
  });

  it('stops routing after detach — no listener left on the window', () => {
    const keys = createHotkeys();
    const hit = vi.fn();
    keys.bind('r', hit);
    keys.attach();
    keys.detach();
    window.dispatchEvent(new KeyboardEvent('keydown', { key: 'r' }));
    expect(hit).not.toHaveBeenCalled();
  });

  it('ignores keys bound before attach until attach is called', () => {
    const keys = createHotkeys();
    const hit = vi.fn();
    keys.bind('x', hit);
    window.dispatchEvent(new KeyboardEvent('keydown', { key: 'x' }));
    expect(hit).not.toHaveBeenCalled();
    keys.attach();
    window.dispatchEvent(new KeyboardEvent('keydown', { key: 'x' }));
    expect(hit).toHaveBeenCalledTimes(1);
    keys.detach();
  });

  it('calls preventDefault on space so the page never scrolls under the factory', () => {
    const keys = createHotkeys();
    keys.bind(' ', () => {});
    keys.attach();
    const ev = new KeyboardEvent('keydown', { key: ' ', cancelable: true });
    window.dispatchEvent(ev);
    expect(ev.defaultPrevented).toBe(true);
    keys.detach();
  });

  it('does not fire a hotkey while the player is typing in a field', () => {
    const keys = createHotkeys();
    const hit = vi.fn();
    keys.bind('r', hit);
    keys.attach();

    const input = document.createElement('input');
    document.body.append(input);
    input.dispatchEvent(new KeyboardEvent('keydown', { key: 'r', bubbles: true }));
    expect(hit).not.toHaveBeenCalled();

    input.remove();
    keys.detach();
  });
});

describe('isTypingTarget', () => {
  it('is true for form fields and contenteditable, false for the world', () => {
    for (const tag of ['input', 'textarea', 'select']) {
      expect(isTypingTarget(document.createElement(tag))).toBe(true);
    }
    const div = document.createElement('div');
    expect(isTypingTarget(div)).toBe(false);

    // The inspector's recipe <select> is exactly why this guard exists.
    const editable = document.createElement('div');
    editable.setAttribute('contenteditable', 'true');
    expect(isTypingTarget(editable)).toBe(true);
  });

  it('is false for null and non-elements', () => {
    expect(isTypingTarget(null)).toBe(false);
    expect(isTypingTarget('r')).toBe(false);
  });
});
