import { render, screen, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import userEvent from '@testing-library/user-event';
import { Filters, FilterOption } from './Filters';

describe('Filters Component', () => {
  const mockOnChange = vi.fn();
  const mockOnReset = vi.fn();

  const mockFilters: FilterOption[] = [
    {
      id: 'category',
      label: 'Category',
      type: 'select',
      options: [
        { value: '1', label: 'Option 1' },
        { value: '2', label: 'Option 2' },
      ],
    },
    {
      id: 'active',
      label: 'Active',
      type: 'checkbox',
    },
    {
      id: 'price',
      label: 'Price',
      type: 'range',
      min: 0,
      max: 100,
      step: 1,
    },
    {
      id: 'date',
      label: 'Date',
      type: 'date',
    },
  ];

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

  it('renders all filter types', () => {
    render(
      <Filters
        filters={mockFilters}
        values={{}}
        onChange={mockOnChange}
        onReset={mockOnReset}
      />,
    );

    expect(screen.getByText('Category')).toBeInTheDocument();
    expect(screen.getByText('Active')).toBeInTheDocument();
    expect(screen.getByText('Price')).toBeInTheDocument();
    expect(screen.getByText('Date')).toBeInTheDocument();
  });

  it('renders select filter with options', () => {
    render(
      <Filters
        filters={[mockFilters[0]]}
        values={{}}
        onChange={mockOnChange}
      />,
    );

    // Le Select utilise un bouton, vérifions qu'il existe
    const buttons = screen.getAllByRole('button');
    const selectButton = buttons.find(
      (btn) => btn.getAttribute('aria-haspopup') === 'true',
    );
    expect(selectButton).toBeInTheDocument();
  });

  it('calls onChange when select filter value changes', async () => {
    const user = userEvent.setup();
    render(
      <Filters
        filters={[mockFilters[0]]}
        values={{}}
        onChange={mockOnChange}
      />,
    );

    const buttons = screen.getAllByRole('button');
    const selectButton = buttons.find(
      (btn) => btn.getAttribute('aria-haspopup') === 'true',
    );
    expect(selectButton).toBeInTheDocument();
    if (selectButton) {
      await user.click(selectButton);

      // Wait for dropdown to appear and select option
      const option1 = await screen.findByText('Option 1');
      await user.click(option1);

      await waitFor(() => {
        expect(mockOnChange).toHaveBeenCalledWith({ category: '1' });
      });
    }
  });

  it('renders checkbox filter', () => {
    render(
      <Filters
        filters={[mockFilters[1]]}
        values={{}}
        onChange={mockOnChange}
      />,
    );

    const checkbox = screen.getByRole('checkbox');
    expect(checkbox).toBeInTheDocument();
    expect(checkbox).not.toBeChecked();
  });

  it('calls onChange when checkbox filter is toggled', async () => {
    const user = userEvent.setup();
    render(
      <Filters
        filters={[mockFilters[1]]}
        values={{}}
        onChange={mockOnChange}
      />,
    );

    const checkbox = screen.getByRole('checkbox');
    await user.click(checkbox);

    expect(mockOnChange).toHaveBeenCalledWith({ active: true });
  });

  it('renders range filter with min and max inputs', () => {
    render(
      <Filters
        filters={[mockFilters[2]]}
        values={{}}
        onChange={mockOnChange}
      />,
    );

    const minInput = screen.getByLabelText(/min:/i);
    const maxInput = screen.getByLabelText(/max:/i);

    expect(minInput).toBeInTheDocument();
    expect(maxInput).toBeInTheDocument();
  });

  it('calls onChange when range filter values change', async () => {
    const user = userEvent.setup();
    render(
      <Filters
        filters={[mockFilters[2]]}
        values={{}}
        onChange={mockOnChange}
      />,
    );

    const minInput = screen.getByLabelText(/min:/i);
    await user.type(minInput, '10', { selectall: true });

    // Le onChange peut être appelé plusieurs fois, vérifions qu'il contient la bonne valeur
    await waitFor(() => {
      expect(mockOnChange).toHaveBeenCalled();
      const calls = mockOnChange.mock.calls;
      const lastCall = calls[calls.length - 1];
      expect(lastCall[0]).toMatchObject({ price: { min: 10, max: 100 } });
    });
  });

  it('renders date filter', () => {
    render(
      <Filters
        filters={[mockFilters[3]]}
        values={{}}
        onChange={mockOnChange}
      />,
    );

    // Le DatePicker utilise un bouton, vérifions qu'il existe
    const buttons = screen.getAllByRole('button');
    const dateButton = buttons.find(
      (btn) => btn.getAttribute('aria-haspopup') === 'true',
    );
    expect(dateButton).toBeInTheDocument();
  });

  it('shows reset button when onReset is provided and filters are active', () => {
    render(
      <Filters
        filters={mockFilters}
        values={{ category: '1', active: true }}
        onChange={mockOnChange}
        onReset={mockOnReset}
      />,
    );

    const resetButton = screen.getByText('Réinitialiser');
    expect(resetButton).toBeInTheDocument();
  });

  it('does not show reset button when no filters are active', () => {
    render(
      <Filters
        filters={mockFilters}
        values={{}}
        onChange={mockOnChange}
        onReset={mockOnReset}
      />,
    );

    const resetButton = screen.queryByText('Réinitialiser');
    expect(resetButton).not.toBeInTheDocument();
  });

  it('calls onReset when reset button is clicked', async () => {
    const user = userEvent.setup();
    render(
      <Filters
        filters={mockFilters}
        values={{ category: '1', active: true }}
        onChange={mockOnChange}
        onReset={mockOnReset}
      />,
    );

    const resetButton = screen.getByText('Réinitialiser');
    await user.click(resetButton);

    expect(mockOnReset).toHaveBeenCalled();
    expect(mockOnChange).toHaveBeenCalledWith({
      category: undefined,
      active: false,
      price: { min: 0, max: 100 },
      date: undefined,
    });
  });

  it('resets all filters to default values', async () => {
    const user = userEvent.setup();
    render(
      <Filters
        filters={mockFilters}
        values={{ category: '1', active: true, price: { min: 20, max: 80 } }}
        onChange={mockOnChange}
        onReset={mockOnReset}
      />,
    );

    const resetButton = screen.getByText('Réinitialiser');
    await user.click(resetButton);

    expect(mockOnChange).toHaveBeenCalledWith({
      category: undefined,
      active: false,
      price: { min: 0, max: 100 },
      date: undefined,
    });
  });

  it('does not show reset button when showReset is false', () => {
    render(
      <Filters
        filters={mockFilters}
        values={{ category: '1' }}
        onChange={mockOnChange}
        onReset={mockOnReset}
        showReset={false}
      />,
    );

    const resetButton = screen.queryByText('Réinitialiser');
    expect(resetButton).not.toBeInTheDocument();
  });

  it('uses custom reset label', () => {
    render(
      <Filters
        filters={mockFilters}
        values={{ category: '1' }}
        onChange={mockOnChange}
        onReset={mockOnReset}
        resetLabel="Clear All"
      />,
    );

    const resetButton = screen.getByText('Clear All');
    expect(resetButton).toBeInTheDocument();
  });

  it('applies custom className', () => {
    const { container } = render(
      <Filters
        filters={mockFilters}
        values={{}}
        onChange={mockOnChange}
        className="custom-class"
      />,
    );

    const filtersContainer = container.firstChild;
    expect(filtersContainer).toHaveClass('custom-class');
  });

  it('handles disabled filters', () => {
    const disabledFilters: FilterOption[] = [
      {
        ...mockFilters[0],
        disabled: true,
      },
    ];

    render(
      <Filters filters={disabledFilters} values={{}} onChange={mockOnChange} />,
    );

    // Le Select utilise un button pour le trigger, cherchons le bouton
    const buttons = screen.getAllByRole('button');
    const selectButton = buttons.find(
      (btn) => btn.getAttribute('aria-haspopup') === 'true',
    );
    expect(selectButton).toBeInTheDocument();
    // Note: Le composant Select peut ne pas désactiver directement le bouton,
    // mais le filtre est désactivé via la prop disabled
    expect(disabledFilters[0].disabled).toBe(true);
  });

  it('displays current filter values', () => {
    render(
      <Filters
        filters={mockFilters}
        values={{
          category: '1',
          active: true,
          price: { min: 20, max: 80 },
        }}
        onChange={mockOnChange}
      />,
    );

    const checkbox = screen.getByRole('checkbox');
    expect(checkbox).toBeChecked();

    const minInput = screen.getByLabelText(/min:/i);
    expect(minInput).toHaveValue(20);

    const maxInput = screen.getByLabelText(/max:/i);
    expect(maxInput).toHaveValue(80);
  });

  it('handles range filter with custom min/max', () => {
    const customRangeFilter: FilterOption = {
      id: 'score',
      label: 'Score',
      type: 'range',
      min: 1,
      max: 10,
      step: 0.1,
    };

    render(
      <Filters
        filters={[customRangeFilter]}
        values={{}}
        onChange={mockOnChange}
      />,
    );

    const minInput = screen.getByLabelText(/min:/i);
    const maxInput = screen.getByLabelText(/max:/i);

    expect(minInput).toHaveAttribute('min', '1');
    expect(minInput).toHaveAttribute('max', '10');
    expect(maxInput).toHaveAttribute('min', '1');
    expect(maxInput).toHaveAttribute('max', '10');
  });
});
