import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { FilterBar } from "./FilterBar";
import { TaskStatus, TaskPriority } from "@mosaic/shared";

describe("FilterBar", (): void => {
  const mockOnFilterChange = vi.fn();

  beforeEach((): void => {
    vi.clearAllMocks();
    vi.useRealTimers(); // Ensure real timers for all tests unless explicitly overridden
  });

  it("should render search input", (): void => {
    render(<FilterBar onFilterChange={mockOnFilterChange} />);
    expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument();
  });

  it("should render status filter", (): void => {
    render(<FilterBar onFilterChange={mockOnFilterChange} />);
    expect(screen.getByRole("button", { name: /status/i })).toBeInTheDocument();
  });

  it("should render priority filter", (): void => {
    render(<FilterBar onFilterChange={mockOnFilterChange} />);
    expect(screen.getByRole("button", { name: /priority/i })).toBeInTheDocument();
  });

  it("should render date range picker", (): void => {
    render(<FilterBar onFilterChange={mockOnFilterChange} />);
    expect(screen.getByPlaceholderText(/from date/i)).toBeInTheDocument();
    expect(screen.getByPlaceholderText(/to date/i)).toBeInTheDocument();
  });

  it("should render clear filters button when filters applied", (): void => {
    render(<FilterBar onFilterChange={mockOnFilterChange} initialFilters={{ search: "test" }} />);
    expect(screen.getByRole("button", { name: /clear filters/i })).toBeInTheDocument();
  });

  it("should not render clear filters button when no filters applied", (): void => {
    render(<FilterBar onFilterChange={mockOnFilterChange} />);
    expect(screen.queryByRole("button", { name: /clear filters/i })).not.toBeInTheDocument();
  });

  it("should debounce search input", async (): Promise<void> => {
    const user = userEvent.setup();

    // Use a debounce long enough that CI environments don't fire it between keystrokes
    render(<FilterBar onFilterChange={mockOnFilterChange} debounceMs={500} />);

    const searchInput = screen.getByPlaceholderText(/search/i);
    mockOnFilterChange.mockClear();

    // Type the first character
    await user.type(searchInput, "t");

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

    // Type more characters quickly (within debounce window)
    await user.type(searchInput, "est");

    // Still should not have been called
    expect(mockOnFilterChange).not.toHaveBeenCalled();

    // Wait for debounce to complete
    await waitFor(
      () => {
        expect(mockOnFilterChange).toHaveBeenCalledWith(
          expect.objectContaining({ search: "test" })
        );
      },
      { timeout: 1000 }
    );

    // Verify it was only called once (debounced)
    expect(mockOnFilterChange).toHaveBeenCalledTimes(1);
  });

  it("should clear all filters when clear button clicked", async (): Promise<void> => {
    const user = userEvent.setup();
    render(
      <FilterBar
        onFilterChange={mockOnFilterChange}
        initialFilters={{
          search: "test",
          status: [TaskStatus.IN_PROGRESS],
          priority: [TaskPriority.HIGH],
        }}
      />
    );

    const clearButton = screen.getByRole("button", { name: /clear filters/i });
    await user.click(clearButton);

    expect(mockOnFilterChange).toHaveBeenCalledWith({});
  });

  it("should handle status selection", async (): Promise<void> => {
    const user = userEvent.setup();
    render(<FilterBar onFilterChange={mockOnFilterChange} />);

    const statusButton = screen.getByRole("button", { name: /status/i });
    await user.click(statusButton);

    // Note: Actual multi-select implementation would need to open a dropdown
    // This is a simplified test
  });

  it("should handle priority selection", async (): Promise<void> => {
    const user = userEvent.setup();
    render(<FilterBar onFilterChange={mockOnFilterChange} />);

    const priorityButton = screen.getByRole("button", { name: /priority/i });
    await user.click(priorityButton);

    // Note: Actual implementation would need to open a dropdown
  });

  it("should handle date range selection", async (): Promise<void> => {
    const user = userEvent.setup();
    render(<FilterBar onFilterChange={mockOnFilterChange} />);

    const fromDate = screen.getByPlaceholderText(/from date/i);
    const toDate = screen.getByPlaceholderText(/to date/i);

    await user.type(fromDate, "2024-01-01");
    await user.type(toDate, "2024-12-31");

    await waitFor(() => {
      expect(mockOnFilterChange).toHaveBeenCalled();
    });
  });

  it("should display active filter count", (): void => {
    render(
      <FilterBar
        onFilterChange={mockOnFilterChange}
        initialFilters={{
          status: [TaskStatus.IN_PROGRESS, TaskStatus.NOT_STARTED],
          priority: [TaskPriority.HIGH],
        }}
      />
    );

    // Should show 3 active filters (2 statuses + 1 priority)
    expect(screen.getByText(/3/)).toBeInTheDocument();
  });

  describe("accessibility (CQ-WEB-11)", (): void => {
    it("should have aria-label on search input", (): void => {
      render(<FilterBar onFilterChange={mockOnFilterChange} />);
      const searchInput = screen.getByRole("textbox", { name: /search tasks/i });
      expect(searchInput).toBeInTheDocument();
    });

    it("should have aria-label on date inputs", (): void => {
      render(<FilterBar onFilterChange={mockOnFilterChange} />);
      expect(screen.getByLabelText(/filter from date/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/filter to date/i)).toBeInTheDocument();
    });

    it("should have aria-labels on status filter buttons", (): void => {
      render(<FilterBar onFilterChange={mockOnFilterChange} />);
      expect(screen.getByRole("button", { name: /status filter/i })).toBeInTheDocument();
    });

    it("should have aria-labels on priority filter buttons", (): void => {
      render(<FilterBar onFilterChange={mockOnFilterChange} />);
      expect(screen.getByRole("button", { name: /priority filter/i })).toBeInTheDocument();
    });

    it("should have id and htmlFor associations on status checkboxes", async (): Promise<void> => {
      const user = userEvent.setup();
      render(<FilterBar onFilterChange={mockOnFilterChange} />);

      // Open status dropdown
      await user.click(screen.getByRole("button", { name: /status filter/i }));

      // Verify specific status checkboxes have proper id attributes
      const notStartedCheckbox = screen.getByLabelText(/filter by status: not started/i);
      expect(notStartedCheckbox).toHaveAttribute("id", "status-filter-NOT_STARTED");

      const inProgressCheckbox = screen.getByLabelText(/filter by status: in progress/i);
      expect(inProgressCheckbox).toHaveAttribute("id", "status-filter-IN_PROGRESS");

      const completedCheckbox = screen.getByLabelText(/filter by status: completed/i);
      expect(completedCheckbox).toHaveAttribute("id", "status-filter-COMPLETED");
    });

    it("should have id and htmlFor associations on priority checkboxes", async (): Promise<void> => {
      const user = userEvent.setup();
      render(<FilterBar onFilterChange={mockOnFilterChange} />);

      // Open priority dropdown
      await user.click(screen.getByRole("button", { name: /priority filter/i }));

      // Verify specific priority checkboxes have proper id attributes
      const lowCheckbox = screen.getByLabelText(/filter by priority: low/i);
      expect(lowCheckbox).toHaveAttribute("id", "priority-filter-LOW");

      const mediumCheckbox = screen.getByLabelText(/filter by priority: medium/i);
      expect(mediumCheckbox).toHaveAttribute("id", "priority-filter-MEDIUM");

      const highCheckbox = screen.getByLabelText(/filter by priority: high/i);
      expect(highCheckbox).toHaveAttribute("id", "priority-filter-HIGH");
    });

    it("should have aria-label on clear filters button", (): void => {
      const filtersWithSearch = { search: "test" };
      render(<FilterBar onFilterChange={mockOnFilterChange} initialFilters={filtersWithSearch} />);
      expect(screen.getByRole("button", { name: /clear filters/i })).toBeInTheDocument();
    });
  });
});
