import { renderHook } from '@testing-library/react';
import { describe, it, expect, vi, afterEach } from 'vitest';

import { useSiteKeyboardShortcuts } from './useSiteKeyboardShortcuts';

function fireKey(key: string, opts: Partial<KeyboardEventInit> = {}) {
  document.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, ...opts }));
}

describe('useSiteKeyboardShortcuts', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('calls onSearch when / is pressed', () => {
    const onSearch = vi.fn();
    renderHook(() => useSiteKeyboardShortcuts({ onSearch }));
    fireKey('/');
    expect(onSearch).toHaveBeenCalledOnce();
  });

  it('calls onNextPage when n is pressed', () => {
    const onNextPage = vi.fn();
    renderHook(() => useSiteKeyboardShortcuts({ onNextPage }));
    fireKey('n');
    expect(onNextPage).toHaveBeenCalledOnce();
  });

  it('calls onPrevPage when p is pressed', () => {
    const onPrevPage = vi.fn();
    renderHook(() => useSiteKeyboardShortcuts({ onPrevPage }));
    fireKey('p');
    expect(onPrevPage).toHaveBeenCalledOnce();
  });

  it('ignores shortcuts when modifier keys are held', () => {
    const onSearch = vi.fn();
    const onNextPage = vi.fn();
    renderHook(() => useSiteKeyboardShortcuts({ onSearch, onNextPage }));

    fireKey('/', { metaKey: true });
    fireKey('n', { ctrlKey: true });
    fireKey('p', { altKey: true });

    expect(onSearch).not.toHaveBeenCalled();
    expect(onNextPage).not.toHaveBeenCalled();
  });

  it('ignores n/p when target is an input element', () => {
    const onNextPage = vi.fn();
    renderHook(() => useSiteKeyboardShortcuts({ onNextPage }));

    const input = document.createElement('input');
    document.body.appendChild(input);
    input.dispatchEvent(new KeyboardEvent('keydown', { key: 'n', bubbles: true }));
    document.body.removeChild(input);

    expect(onNextPage).not.toHaveBeenCalled();
  });

  it('ignores / when target is an input element', () => {
    const onSearch = vi.fn();
    renderHook(() => useSiteKeyboardShortcuts({ onSearch }));

    const input = document.createElement('input');
    document.body.appendChild(input);
    input.dispatchEvent(new KeyboardEvent('keydown', { key: '/', bubbles: true }));
    document.body.removeChild(input);

    expect(onSearch).not.toHaveBeenCalled();
  });

  it('ignores shortcuts when target is a textarea', () => {
    const onSearch = vi.fn();
    const onNextPage = vi.fn();
    renderHook(() => useSiteKeyboardShortcuts({ onSearch, onNextPage }));

    const textarea = document.createElement('textarea');
    document.body.appendChild(textarea);
    textarea.dispatchEvent(new KeyboardEvent('keydown', { key: '/', bubbles: true }));
    textarea.dispatchEvent(new KeyboardEvent('keydown', { key: 'n', bubbles: true }));
    document.body.removeChild(textarea);

    expect(onSearch).not.toHaveBeenCalled();
    expect(onNextPage).not.toHaveBeenCalled();
  });

  it('does nothing when no callbacks provided', () => {
    renderHook(() => useSiteKeyboardShortcuts({}));
    // Should not throw
    fireKey('/');
    fireKey('n');
    fireKey('p');
  });

  it('cleans up event listener on unmount', () => {
    const onSearch = vi.fn();
    const { unmount } = renderHook(() => useSiteKeyboardShortcuts({ onSearch }));
    unmount();
    fireKey('/');
    expect(onSearch).not.toHaveBeenCalled();
  });
});
