import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { SearchInput } from "../SearchInput";

describe("SearchInput", () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.restoreAllMocks();
    vi.useRealTimers();
  });

  it("should render search input field", () => {
    render(<SearchInput onSearch={vi.fn()} />);

    const input = screen.getByPlaceholderText(/search/i);
    expect(input).toBeInTheDocument();
  });

  it("should call onSearch after debounce delay", () => {
    const onSearch = vi.fn();
    render(<SearchInput onSearch={onSearch} debounceMs={300} />);

    const input = screen.getByPlaceholderText(/search/i);
    fireEvent.change(input, { target: { value: "test query" } });

    // Should not call immediately
    expect(onSearch).not.toHaveBeenCalled();

    // Fast-forward time
    vi.advanceTimersByTime(300);

    expect(onSearch).toHaveBeenCalledWith("test query");
  });

  it("should debounce multiple keystrokes", () => {
    const onSearch = vi.fn();
    render(<SearchInput onSearch={onSearch} debounceMs={300} />);

    const input = screen.getByPlaceholderText(/search/i);

    fireEvent.change(input, { target: { value: "a" } });
    vi.advanceTimersByTime(100);
    fireEvent.change(input, { target: { value: "ab" } });
    vi.advanceTimersByTime(100);
    fireEvent.change(input, { target: { value: "abc" } });
    vi.advanceTimersByTime(100);

    // Should only call once after final delay
    expect(onSearch).not.toHaveBeenCalled();

    vi.advanceTimersByTime(300);

    expect(onSearch).toHaveBeenCalledTimes(1);
    expect(onSearch).toHaveBeenCalledWith("abc");
  });

  it("should focus input when Cmd+K is pressed", () => {
    render(<SearchInput onSearch={vi.fn()} />);

    const input = screen.getByPlaceholderText(/search/i);

    // Simulate Cmd+K
    fireEvent.keyDown(document, { key: "k", metaKey: true });

    expect(document.activeElement).toBe(input);
  });

  it("should focus input when Ctrl+K is pressed on Windows/Linux", () => {
    render(<SearchInput onSearch={vi.fn()} />);

    const input = screen.getByPlaceholderText(/search/i);

    // Simulate Ctrl+K
    fireEvent.keyDown(document, { key: "k", ctrlKey: true });

    expect(document.activeElement).toBe(input);
  });

  it("should clear input when Escape is pressed", () => {
    const onSearch = vi.fn();
    render(<SearchInput onSearch={onSearch} />);

    const input = screen.getByPlaceholderText(/search/i);

    fireEvent.change(input, { target: { value: "test query" } });
    expect((input as HTMLInputElement).value).toBe("test query");

    fireEvent.keyDown(input, { key: "Escape" });

    expect((input as HTMLInputElement).value).toBe("");
  });

  it("should display initial value", () => {
    render(<SearchInput onSearch={vi.fn()} initialValue="initial query" />);

    const input = screen.getByPlaceholderText(/search/i);
    expect((input as HTMLInputElement).value).toBe("initial query");
  });

  it("should show loading indicator when isLoading is true", () => {
    render(<SearchInput onSearch={vi.fn()} isLoading={true} />);

    const loader = screen.getByTestId("search-loading");
    expect(loader).toBeInTheDocument();
  });

  it("should not call onSearch for empty strings", () => {
    const onSearch = vi.fn();
    render(<SearchInput onSearch={onSearch} debounceMs={300} />);

    const input = screen.getByPlaceholderText(/search/i);
    fireEvent.change(input, { target: { value: "   " } });

    vi.advanceTimersByTime(300);

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

  it("should show keyboard shortcut hint", () => {
    render(<SearchInput onSearch={vi.fn()} />);

    const hint = screen.getByText(/⌘K|Ctrl\+K/i);
    expect(hint).toBeInTheDocument();
  });
});
