import { describe, it, expect, vi } from "vitest";
import * as React from "react";
import { screen, fireEvent, waitFor } from "@testing-library/react";
import { render } from 'vitest-browser-react';
import userEvent from "@testing-library/user-event";
import { AutoComplete } from "./AutoComplete";

// Mock data
const mockItems = [
  { id: 1, name: "Apple" },
  { id: 2, name: "Banana" },
  { id: 3, name: "Cherry" },
  { id: 4, name: "Ananas" },
];

// Mock fetchSuggestions function with a delay and explicit return type
const mockFetchSuggestions = vi.fn((query: string) => {
  return new Promise<{ items: typeof mockItems; totalResults: number }>((resolve) => {
    setTimeout(() => {
      const filtered = mockItems.filter((item) =>
        item.name.toLowerCase().includes(query.toLowerCase())
      );
      resolve({
        items: filtered,
        totalResults: filtered.length,
      });
    }, 500); // Simulate a 500ms delay
  });
});

// Mock renderSuggestion function
const mockRenderSuggestion = (item: { id: number; name: string }) => (
  <div>{item.name}</div>
);

describe("AutoComplete Component", () => {
  it("renders AutoComplete component", () => {
    render(
      <AutoComplete
        fetchSuggestions={mockFetchSuggestions}
        renderSuggestion={mockRenderSuggestion}
        valueProp="name"
      />
    );

    expect(screen.getByPlaceholderText("Search...")).toBeInTheDocument();
  });

  it("displays suggestions when user types", async () => {
    render(
      <AutoComplete
        fetchSuggestions={mockFetchSuggestions}
        renderSuggestion={mockRenderSuggestion}
        valueProp="name"
      />
    );

    const input = screen.getByPlaceholderText("Search...");
    userEvent.type(input, "a");

    await waitFor(() => {
      expect(mockFetchSuggestions).toHaveBeenCalledWith("a", expect.any(Object));
    });

    await waitFor(() => {
      expect(screen.getByText("Apple")).toBeInTheDocument();
      expect(screen.getByText("Banana")).toBeInTheDocument();
    });
  });

  it("selects an item from suggestions", async () => {
    render(
      <AutoComplete
        fetchSuggestions={mockFetchSuggestions}
        renderSuggestion={mockRenderSuggestion}
        valueProp="name"
      />
    );

    const input = screen.getByPlaceholderText("Search...");
    userEvent.type(input, "a");

    await waitFor(() => {
      expect(screen.getByText("Apple")).toBeInTheDocument();
    });

    const appleSuggestion = screen.getByText("Apple");
    userEvent.click(appleSuggestion);

    await waitFor(() => {
      expect(input).toHaveValue("Apple");
    });
  });

  it("handles error when fetching suggestions fails", async () => {
    const mockFetchSuggestionsWithError = vi.fn(() => {
      return new Promise<{ items: typeof mockItems; totalResults: number }>((_, reject) => {
        setTimeout(() => {
          reject(new Error("Failed to fetch suggestions"));
        }, 500); // Simulate a 500ms delay
      });
    });

    render(
      <AutoComplete
        fetchSuggestions={mockFetchSuggestionsWithError}
        renderSuggestion={mockRenderSuggestion}
        valueProp="name"
      />
    );

    const input = screen.getByPlaceholderText("Search...");
    userEvent.type(input, "a");

    await waitFor(() => {
      expect(screen.getByText("Failed to fetch suggestions")).toBeInTheDocument();
    });
  });

  it("clears suggestions when input is cleared", async () => {
    render(
      <AutoComplete
        fetchSuggestions={mockFetchSuggestions}
        renderSuggestion={mockRenderSuggestion}
        valueProp="name"
      />
    );

    const input = screen.getByPlaceholderText("Search...");
    userEvent.type(input, "a");

    await waitFor(() => {
      expect(screen.getByText("Apple")).toBeInTheDocument();
    });

    userEvent.clear(input);

    await waitFor(() => {
      expect(screen.queryByText("Apple")).not.toBeInTheDocument();
    });
  });

  it("navigates suggestions with keyboard", async () => {
    render(
      <AutoComplete
        fetchSuggestions={mockFetchSuggestions}
        renderSuggestion={mockRenderSuggestion}
        valueProp="name"
      />
    );

    const input = screen.getByPlaceholderText("Search...");
    userEvent.type(input, "a");

    await waitFor(() => {
      expect(screen.getByText("Banana")).toBeInTheDocument();
    });

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

    expect(input).toHaveValue("Banana");
  });
});