import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import ItemAutocomplete from "./ItemAutocomplete";
import type { ItemRecord } from "@/lib/api";
import * as api from "@/lib/api";

// Mock the API module
vi.mock("@/lib/api", () => ({
  searchItems: vi.fn(),
}));

describe("ItemAutocomplete", () => {
  const mockItems: ItemRecord[] = [
    {
      _id: "1",
      itemCode: "ITEM-001",
      itemName: "Test Item 1",
      description: null,
      brand: null,
      barcode: null,
      countryOfOrigin: null,
      blend: null,
      grainType: null,
      varietyType: null,
      processType: null,
      unit: null,
      bagWeightKg: null,
      packing: null,
      variant: null,
      riceName: null,
      category: null,
      status: null,
    },
    {
      _id: "2",
      itemCode: "ITEM-002",
      itemName: "Test Item 2",
      description: null,
      brand: null,
      barcode: null,
      countryOfOrigin: null,
      blend: null,
      grainType: null,
      varietyType: null,
      processType: null,
      unit: null,
      bagWeightKg: null,
      packing: null,
      variant: null,
      riceName: null,
      category: null,
      status: null,
    },
  ];

  const mockOnSelect = vi.fn();
  const mockOnChange = vi.fn();

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

  it("renders input with placeholder", () => {
    render(
      <ItemAutocomplete
        value=""
        onSelect={mockOnSelect}
        onChange={mockOnChange}
        placeholder="Search items..."
      />
    );

    const input = screen.getByPlaceholderText("Search items...");
    expect(input).toBeDefined();
  });

  it("calls onChange when typing", () => {
    render(
      <ItemAutocomplete
        value=""
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    const input = screen.getByPlaceholderText("Enter item code or name...");
    fireEvent.change(input, { target: { value: "IT" } });

    expect(mockOnChange).toHaveBeenCalledWith("IT");
  });

  it("does not search with less than 2 characters", () => {
    render(
      <ItemAutocomplete
        value="I"
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Should not call API with less than 2 characters
    expect(api.searchItems).not.toHaveBeenCalled();
  });

  it("displays suggestions after successful search", async () => {
    vi.mocked(api.searchItems).mockResolvedValue(mockItems);

    const { rerender } = render(
      <ItemAutocomplete
        value=""
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Type search query
    rerender(
      <ItemAutocomplete
        value="ITEM"
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Wait for suggestions to appear (debounce + API call)
    await waitFor(
      () => {
        expect(screen.getByText("ITEM-001")).toBeDefined();
      },
      { timeout: 1000 }
    );

    expect(screen.getByText("Test Item 1")).toBeDefined();
    expect(screen.getByText("ITEM-002")).toBeDefined();
    expect(screen.getByText("Test Item 2")).toBeDefined();
  });

  it("shows 'No results found' when search returns empty", async () => {
    vi.mocked(api.searchItems).mockResolvedValue([]);

    const { rerender } = render(
      <ItemAutocomplete
        value=""
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Type search query
    rerender(
      <ItemAutocomplete
        value="NONEXISTENT"
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Wait for "No results found" message
    await waitFor(
      () => {
        expect(screen.getByText("No results found")).toBeDefined();
      },
      { timeout: 1000 }
    );
  });

  it("calls onSelect when clicking a suggestion", async () => {
    vi.mocked(api.searchItems).mockResolvedValue(mockItems);

    const { rerender } = render(
      <ItemAutocomplete
        value=""
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Type search query
    rerender(
      <ItemAutocomplete
        value="ITEM"
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Wait for suggestions and click first one
    await waitFor(
      () => {
        const suggestion = screen.getByText("ITEM-001");
        fireEvent.click(suggestion);
      },
      { timeout: 1000 }
    );

    expect(mockOnSelect).toHaveBeenCalledWith(mockItems[0]);
  });

  it("supports keyboard navigation with Arrow Down and Enter", async () => {
    vi.mocked(api.searchItems).mockResolvedValue(mockItems);

    const { rerender } = render(
      <ItemAutocomplete
        value=""
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Type search query
    rerender(
      <ItemAutocomplete
        value="ITEM"
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Wait for suggestions
    await waitFor(
      () => {
        expect(screen.getByText("ITEM-001")).toBeDefined();
      },
      { timeout: 1000 }
    );

    const input = screen.getByPlaceholderText("Enter item code or name...");

    // Press Arrow Down to select first item
    fireEvent.keyDown(input, { key: "ArrowDown" });

    // Press Enter to select
    fireEvent.keyDown(input, { key: "Enter" });

    expect(mockOnSelect).toHaveBeenCalledWith(mockItems[0]);
  });

  it("closes dropdown on Escape key", async () => {
    vi.mocked(api.searchItems).mockResolvedValue(mockItems);

    const { rerender } = render(
      <ItemAutocomplete
        value=""
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Type search query
    rerender(
      <ItemAutocomplete
        value="ITEM"
        onSelect={mockOnSelect}
        onChange={mockOnChange}
      />
    );

    // Wait for suggestions
    await waitFor(
      () => {
        expect(screen.getByText("ITEM-001")).toBeDefined();
      },
      { timeout: 1000 }
    );

    const input = screen.getByPlaceholderText("Enter item code or name...");

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

    // Suggestions should be hidden
    await waitFor(() => {
      expect(screen.queryByText("ITEM-001")).toBeNull();
    });
  });

  it("is disabled when disabled prop is true", () => {
    render(
      <ItemAutocomplete
        value=""
        onSelect={mockOnSelect}
        onChange={mockOnChange}
        disabled={true}
      />
    );

    const input = screen.getByPlaceholderText("Enter item code or name...") as HTMLInputElement;
    expect(input.disabled).toBe(true);
  });

  it("does not search when disabled", () => {
    render(
      <ItemAutocomplete
        value="ITEM"
        onSelect={mockOnSelect}
        onChange={mockOnChange}
        disabled={true}
      />
    );

    // Should not call API when disabled
    expect(api.searchItems).not.toHaveBeenCalled();
  });
});
