/**
 * Tests for CommandPalette component.
 *
 * TDD: These tests define the expected behavior before implementation.
 */

import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { CommandPalette } from "@/components/CommandPalette";
import type { FocusItem } from "@/lib/types";

const mockItems: FocusItem[] = [
  {
    id: "mr-1",
    title: "Fix auth token refresh",
    type: "mr_review",
    project: "platform/core",
    url: "https://gitlab.com/platform/core/-/merge_requests/1",
    iid: 1,
    updatedAt: "2026-02-25T10:00:00Z",
    contextQuote: null,
    requestedBy: "alice",
  },
  {
    id: "mr-2",
    title: "Update README",
    type: "mr_authored",
    project: "platform/docs",
    url: "https://gitlab.com/platform/docs/-/merge_requests/2",
    iid: 2,
    updatedAt: "2026-02-20T10:00:00Z",
    contextQuote: null,
    requestedBy: null,
  },
  {
    id: "issue-1",
    title: "Auth bug in production",
    type: "issue",
    project: "platform/core",
    url: "https://gitlab.com/platform/core/-/issues/42",
    iid: 42,
    updatedAt: "2026-02-24T10:00:00Z",
    contextQuote: null,
    requestedBy: null,
  },
  {
    id: "manual-1",
    title: "Review quarterly goals",
    type: "manual",
    project: "personal",
    url: "",
    iid: 0,
    updatedAt: null,
    contextQuote: null,
    requestedBy: null,
  },
];

describe("CommandPalette", () => {
  const defaultProps = {
    isOpen: true,
    items: mockItems,
    onFilter: vi.fn(),
    onSelect: vi.fn(),
    onClose: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("renders when open", () => {
    render(<CommandPalette {...defaultProps} />);

    expect(screen.getByRole("dialog")).toBeInTheDocument();
    expect(
      screen.getByPlaceholderText(/Search or filter/i)
    ).toBeInTheDocument();
  });

  it("does not render when closed", () => {
    render(<CommandPalette {...defaultProps} isOpen={false} />);

    expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
  });

  it("closes on Escape key", async () => {
    const user = userEvent.setup();
    const onClose = vi.fn();
    render(<CommandPalette {...defaultProps} onClose={onClose} />);

    await user.keyboard("{Escape}");

    expect(onClose).toHaveBeenCalled();
  });

  it("closes when clicking backdrop", async () => {
    const user = userEvent.setup();
    const onClose = vi.fn();
    render(<CommandPalette {...defaultProps} onClose={onClose} />);

    // Click the backdrop (outer div)
    await user.click(screen.getByTestId("command-palette-backdrop"));

    expect(onClose).toHaveBeenCalled();
  });

  describe("text search", () => {
    it("filters items by text", async () => {
      const user = userEvent.setup();
      render(<CommandPalette {...defaultProps} />);

      await user.type(screen.getByRole("textbox"), "auth");

      // Should show items containing "auth"
      expect(screen.getByText("Fix auth token refresh")).toBeInTheDocument();
      expect(screen.getByText("Auth bug in production")).toBeInTheDocument();
      // Should not show items without "auth"
      expect(screen.queryByText("Update README")).not.toBeInTheDocument();
      expect(
        screen.queryByText("Review quarterly goals")
      ).not.toBeInTheDocument();
    });

    it("shows all items when search is empty", () => {
      render(<CommandPalette {...defaultProps} />);

      // Should show all items (or top 10)
      expect(screen.getByText("Fix auth token refresh")).toBeInTheDocument();
      expect(screen.getByText("Update README")).toBeInTheDocument();
    });

    it("selects item and closes on click", async () => {
      const user = userEvent.setup();
      const onSelect = vi.fn();
      const onClose = vi.fn();
      render(
        <CommandPalette
          {...defaultProps}
          onSelect={onSelect}
          onClose={onClose}
        />
      );

      await user.click(screen.getByText("Fix auth token refresh"));

      expect(onSelect).toHaveBeenCalledWith("mr-1");
      expect(onClose).toHaveBeenCalled();
    });

    it("selects item on Enter when typing", async () => {
      const user = userEvent.setup();
      const onSelect = vi.fn();
      const onClose = vi.fn();
      render(
        <CommandPalette
          {...defaultProps}
          onSelect={onSelect}
          onClose={onClose}
        />
      );

      // Type to filter to one item
      await user.type(screen.getByRole("textbox"), "README");
      await user.keyboard("{Enter}");

      expect(onSelect).toHaveBeenCalledWith("mr-2");
      expect(onClose).toHaveBeenCalled();
    });
  });

  describe("filter commands", () => {
    it("shows filter command options when typing type:", async () => {
      const user = userEvent.setup();
      render(<CommandPalette {...defaultProps} />);

      await user.type(screen.getByRole("textbox"), "type:");

      expect(screen.getByText("type:mr_review")).toBeInTheDocument();
      expect(screen.getByText("type:issue")).toBeInTheDocument();
      expect(screen.getByText("type:manual")).toBeInTheDocument();
    });

    it("filters by type when type: command used", async () => {
      const user = userEvent.setup();
      const onFilter = vi.fn();
      const onClose = vi.fn();
      render(
        <CommandPalette
          {...defaultProps}
          onFilter={onFilter}
          onClose={onClose}
        />
      );

      await user.type(screen.getByRole("textbox"), "type:");
      await user.click(screen.getByText("type:mr_review"));

      expect(onFilter).toHaveBeenCalledWith({ type: "mr_review" });
      expect(onClose).toHaveBeenCalled();
    });

    it("shows staleness filter options when typing stale:", async () => {
      const user = userEvent.setup();
      render(<CommandPalette {...defaultProps} />);

      await user.type(screen.getByRole("textbox"), "stale:");

      expect(screen.getByText("stale:1d")).toBeInTheDocument();
      expect(screen.getByText("stale:3d")).toBeInTheDocument();
      expect(screen.getByText("stale:7d")).toBeInTheDocument();
    });

    it("filters by staleness when stale: command used", async () => {
      const user = userEvent.setup();
      const onFilter = vi.fn();
      const onClose = vi.fn();
      render(
        <CommandPalette
          {...defaultProps}
          onFilter={onFilter}
          onClose={onClose}
        />
      );

      await user.type(screen.getByRole("textbox"), "stale:");
      await user.click(screen.getByText("stale:7d"));

      expect(onFilter).toHaveBeenCalledWith({ minAge: 7 });
      expect(onClose).toHaveBeenCalled();
    });

    it("shows command suggestions before typing a command", () => {
      render(<CommandPalette {...defaultProps} />);

      // Commands section header should exist
      expect(screen.getByText("Commands")).toBeInTheDocument();
      // Command buttons should be present
      expect(screen.getByText("type:...")).toBeInTheDocument();
      expect(screen.getByText("stale:...")).toBeInTheDocument();
    });
  });

  describe("keyboard navigation", () => {
    it("navigates down with arrow key", async () => {
      const user = userEvent.setup();
      render(<CommandPalette {...defaultProps} />);

      const input = screen.getByRole("textbox");
      await user.click(input);
      await user.keyboard("{ArrowDown}");

      // First item should be highlighted
      const items = screen.getAllByRole("option");
      expect(items[0]).toHaveAttribute("data-highlighted", "true");
    });

    it("navigates up with arrow key", async () => {
      const user = userEvent.setup();
      render(<CommandPalette {...defaultProps} />);

      const input = screen.getByRole("textbox");
      await user.click(input);
      await user.keyboard("{ArrowDown}{ArrowDown}{ArrowUp}");

      // First item should be highlighted again
      const items = screen.getAllByRole("option");
      expect(items[0]).toHaveAttribute("data-highlighted", "true");
    });

    it("selects highlighted item on Enter", async () => {
      const user = userEvent.setup();
      const onSelect = vi.fn();
      render(<CommandPalette {...defaultProps} onSelect={onSelect} />);

      const input = screen.getByRole("textbox");
      await user.click(input);
      await user.keyboard("{ArrowDown}{Enter}");

      expect(onSelect).toHaveBeenCalledWith("mr-1");
    });
  });

  describe("empty state", () => {
    it("shows no results message when no items match", async () => {
      const user = userEvent.setup();
      render(<CommandPalette {...defaultProps} />);

      await user.type(screen.getByRole("textbox"), "xyznonexistent");

      expect(screen.getByText(/No results found/i)).toBeInTheDocument();
    });
  });
});
