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

describe('TrackFilters', () => {
  const mockOnFiltersChange = vi.fn();
  const mockOnSearchChange = vi.fn();

  const defaultProps = {
    filters: {} as TrackFilterOptions,
    onFiltersChange: mockOnFiltersChange,
    searchQuery: '',
    onSearchChange: mockOnSearchChange,
  };

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

  it('should render track filters', () => {
    render(<TrackFilters {...defaultProps} />);
    expect(screen.getByLabelText('Rechercher des pistes')).toBeInTheDocument();
  });

  it('should display search input when showSearch is true', () => {
    render(<TrackFilters {...defaultProps} showSearch={true} />);
    expect(screen.getByLabelText('Rechercher des pistes')).toBeInTheDocument();
  });

  it('should not display search input when showSearch is false', () => {
    render(<TrackFilters {...defaultProps} showSearch={false} />);
    expect(
      screen.queryByLabelText('Rechercher des pistes'),
    ).not.toBeInTheDocument();
  });

  it('should display genre filter when showGenre is true', () => {
    render(
      <TrackFilters
        {...defaultProps}
        showGenre={true}
        availableGenres={['Rock', 'Pop']}
      />,
    );
    expect(screen.getByLabelText('Filtrer par genre')).toBeInTheDocument();
  });

  it('should not display genre filter when showGenre is false', () => {
    render(<TrackFilters {...defaultProps} showGenre={false} />);
    expect(
      screen.queryByLabelText('Filtrer par genre'),
    ).not.toBeInTheDocument();
  });

  it('should display artist filter when showArtist is true', () => {
    render(
      <TrackFilters
        {...defaultProps}
        showArtist={true}
        availableArtists={['Artist 1', 'Artist 2']}
      />,
    );
    expect(screen.getByLabelText('Filtrer par artiste')).toBeInTheDocument();
  });

  it('should not display artist filter when showArtist is false', () => {
    render(<TrackFilters {...defaultProps} showArtist={false} />);
    expect(
      screen.queryByLabelText('Filtrer par artiste'),
    ).not.toBeInTheDocument();
  });

  it('should display year filter when showYear is true', () => {
    render(
      <TrackFilters
        {...defaultProps}
        showYear={true}
        availableYears={[2020, 2021]}
      />,
    );
    expect(screen.getByLabelText('Filtrer par année')).toBeInTheDocument();
  });

  it('should not display year filter when showYear is false', () => {
    render(<TrackFilters {...defaultProps} showYear={false} />);
    expect(
      screen.queryByLabelText('Filtrer par année'),
    ).not.toBeInTheDocument();
  });

  it('should display duration filter when showDuration is true', () => {
    render(<TrackFilters {...defaultProps} showDuration={true} />);
    expect(
      screen.getByLabelText('Durée minimale en minutes'),
    ).toBeInTheDocument();
    expect(
      screen.getByLabelText('Durée maximale en minutes'),
    ).toBeInTheDocument();
  });

  it('should not display duration filter when showDuration is false', () => {
    render(<TrackFilters {...defaultProps} showDuration={false} />);
    expect(
      screen.queryByLabelText('Durée minimale en minutes'),
    ).not.toBeInTheDocument();
    expect(
      screen.queryByLabelText('Durée maximale en minutes'),
    ).not.toBeInTheDocument();
  });

  it('should call onSearchChange when search input changes', async () => {
    const user = userEvent.setup();
    render(<TrackFilters {...defaultProps} />);

    const searchInput = screen.getByLabelText('Rechercher des pistes');
    await user.type(searchInput, 'test query');

    expect(mockOnSearchChange).toHaveBeenCalled();
  });

  it('should call onFiltersChange when genre filter changes', async () => {
    const user = userEvent.setup();
    render(
      <TrackFilters
        {...defaultProps}
        availableGenres={['Rock', 'Pop', 'Jazz']}
      />,
    );

    const genreSelect = screen.getByLabelText('Filtrer par genre');
    await user.selectOptions(genreSelect, 'Rock');

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

  it('should call onFiltersChange when artist filter changes', async () => {
    const user = userEvent.setup();
    render(
      <TrackFilters
        {...defaultProps}
        availableArtists={['Artist 1', 'Artist 2']}
      />,
    );

    const artistSelect = screen.getByLabelText('Filtrer par artiste');
    await user.selectOptions(artistSelect, 'Artist 1');

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

  it('should call onFiltersChange when year filter changes', async () => {
    const user = userEvent.setup();
    render(
      <TrackFilters {...defaultProps} availableYears={[2020, 2021, 2022]} />,
    );

    const yearSelect = screen.getByLabelText('Filtrer par année');
    await user.selectOptions(yearSelect, '2021');

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

  it('should call onFiltersChange when duration filters change', async () => {
    const user = userEvent.setup();
    render(<TrackFilters {...defaultProps} />);

    const minDurationInput = screen.getByLabelText('Durée minimale en minutes');
    await user.type(minDurationInput, '2');

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

  it('should display available genres in select', () => {
    render(
      <TrackFilters
        {...defaultProps}
        availableGenres={['Rock', 'Pop', 'Jazz']}
      />,
    );

    expect(screen.getByText('Rock')).toBeInTheDocument();
    expect(screen.getByText('Pop')).toBeInTheDocument();
    expect(screen.getByText('Jazz')).toBeInTheDocument();
  });

  it('should display available artists in select', () => {
    render(
      <TrackFilters
        {...defaultProps}
        availableArtists={['Artist 1', 'Artist 2']}
      />,
    );

    expect(screen.getByText('Artist 1')).toBeInTheDocument();
    expect(screen.getByText('Artist 2')).toBeInTheDocument();
  });

  it('should display available years in select', () => {
    render(
      <TrackFilters {...defaultProps} availableYears={[2020, 2021, 2022]} />,
    );

    expect(screen.getByText('2020')).toBeInTheDocument();
    expect(screen.getByText('2021')).toBeInTheDocument();
    expect(screen.getByText('2022')).toBeInTheDocument();
  });

  it('should use text input for artist when no available artists provided', () => {
    render(<TrackFilters {...defaultProps} availableArtists={[]} />);

    const artistInput = screen.getByLabelText('Filtrer par artiste');
    expect(artistInput).toHaveAttribute('type', 'text');
  });

  it('should use number input for year when no available years provided', () => {
    render(<TrackFilters {...defaultProps} availableYears={[]} />);

    const yearInput = screen.getByLabelText('Filtrer par année');
    expect(yearInput).toHaveAttribute('type', 'number');
  });

  it('should display clear filters button when filters are active', () => {
    render(<TrackFilters {...defaultProps} filters={{ genre: 'Rock' }} />);

    expect(
      screen.getByLabelText('Effacer tous les filtres'),
    ).toBeInTheDocument();
  });

  it('should not display clear filters button when no filters are active', () => {
    render(<TrackFilters {...defaultProps} filters={{}} searchQuery="" />);

    expect(
      screen.queryByLabelText('Effacer tous les filtres'),
    ).not.toBeInTheDocument();
  });

  it('should clear all filters when clear button is clicked', async () => {
    const user = userEvent.setup();
    render(
      <TrackFilters
        {...defaultProps}
        filters={{ genre: 'Rock', artist: 'Artist 1' }}
        searchQuery="test"
      />,
    );

    const clearButton = screen.getByLabelText('Effacer tous les filtres');
    await user.click(clearButton);

    expect(mockOnFiltersChange).toHaveBeenCalledWith({});
    expect(mockOnSearchChange).toHaveBeenCalledWith('');
  });

  it('should be collapsible when collapsible prop is true', () => {
    render(<TrackFilters {...defaultProps} collapsible={true} />);

    expect(screen.getByText('Filtres')).toBeInTheDocument();
    // Le texte "Masquer" ou "Afficher" est dans un span, donc on cherche par texte partiel
    expect(screen.getByText(/Masquer|Afficher/)).toBeInTheDocument();
  });

  it('should toggle expanded state when collapsible header is clicked', async () => {
    const user = userEvent.setup();
    render(<TrackFilters {...defaultProps} collapsible={true} />);

    // Trouver le bouton par son aria-expanded
    const toggleButton = screen.getByRole('button', { name: /Filtres/i });
    expect(toggleButton).toHaveAttribute('aria-expanded', 'true');

    await user.click(toggleButton);

    // Après le clic, le bouton devrait être collapsed
    await waitFor(() => {
      expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
    });
  });

  it('should display active filters badge when filters are active', () => {
    render(
      <TrackFilters
        {...defaultProps}
        filters={{ genre: 'Rock' }}
        collapsible={true}
      />,
    );

    expect(screen.getByText('Actifs')).toBeInTheDocument();
  });

  it('should apply custom className', () => {
    const { container } = render(
      <TrackFilters {...defaultProps} className="custom-class" />,
    );
    expect(container.firstChild).toHaveClass('custom-class');
  });

  it('should update filters when props change', () => {
    const { rerender } = render(
      <TrackFilters
        {...defaultProps}
        filters={{ genre: 'Rock' }}
        availableGenres={['Rock', 'Pop', 'Jazz']}
      />,
    );

    let genreSelect = screen.getByLabelText(
      'Filtrer par genre',
    ) as HTMLSelectElement;
    expect(genreSelect.value).toBe('Rock');

    rerender(
      <TrackFilters
        {...defaultProps}
        filters={{ genre: 'Pop' }}
        availableGenres={['Rock', 'Pop', 'Jazz']}
      />,
    );

    // Re-quérir le select après rerender
    genreSelect = screen.getByLabelText(
      'Filtrer par genre',
    ) as HTMLSelectElement;
    expect(genreSelect.value).toBe('Pop');
  });

  it('should update search query when prop changes', () => {
    const { rerender } = render(
      <TrackFilters {...defaultProps} searchQuery="initial" />,
    );

    const searchInput = screen.getByLabelText(
      'Rechercher des pistes',
    ) as HTMLInputElement;
    expect(searchInput.value).toBe('initial');

    rerender(<TrackFilters {...defaultProps} searchQuery="updated" />);

    expect(searchInput.value).toBe('updated');
  });
});
