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

// Mock useTrackList
const mockUseTrackList = vi.fn();
vi.mock('../hooks/useTrackList', () => ({
  useTrackList: (options: unknown) => mockUseTrackList(options),
}));

// Mock useSearchParams
const mockSetSearchParams = vi.fn();
const mockSearchParams = new URLSearchParams();
vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    useSearchParams: () => [mockSearchParams, mockSetSearchParams],
  };
});

const mockTracks: Track[] = [
  {
    id: 1,
    title: 'Track 1',
    artist: 'Artist 1',
    album: 'Album 1',
    duration: 180,
    url: 'https://example.com/track1.mp3',
    genre: 'Rock',
  },
  {
    id: 2,
    title: 'Track 2',
    artist: 'Artist 2',
    album: 'Album 2',
    duration: 240,
    url: 'https://example.com/track2.mp3',
    genre: 'Pop',
  },
];

const defaultTrackListReturn = {
  tracks: mockTracks,
  filteredTracks: mockTracks,
  displayMode: 'list' as const,
  sortOptions: { field: 'title' as const, order: 'asc' as const },
  filterOptions: {},
  isLoading: false,
  error: null,
  pagination: { page: 1, limit: 20 },
  total: 2,
  totalPages: 1,
  setTracks: vi.fn(),
  setDisplayMode: vi.fn(),
  setSortField: vi.fn(),
  setSortOrder: vi.fn(),
  setFilterOptions: vi.fn(),
  clearFilters: vi.fn(),
  setPagination: vi.fn(),
  setPage: vi.fn(),
  setLimit: vi.fn(),
  setSearchQuery: vi.fn(),
  addTrack: vi.fn(),
  removeTrack: vi.fn(),
  updateTrack: vi.fn(),
  loadTracks: vi.fn(),
  refreshTracks: vi.fn(),
  searchQuery: '',
};

describe('TrackListContainer', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockUseTrackList.mockReturnValue(defaultTrackListReturn);
  });

  it('should render track list container', () => {
    render(<TrackListContainer />);
    expect(
      screen.getByRole('group', { name: 'Options de tri' }),
    ).toBeInTheDocument();
  });

  it('should display filters when showFilters is true', () => {
    render(<TrackListContainer showFilters={true} />);
    expect(screen.getByLabelText('Rechercher des pistes')).toBeInTheDocument();
  });

  it('should not display filters when showFilters is false', () => {
    render(<TrackListContainer showFilters={false} />);
    expect(
      screen.queryByLabelText('Rechercher des pistes'),
    ).not.toBeInTheDocument();
  });

  it('should display sort when showSort is true', () => {
    render(<TrackListContainer showSort={true} />);
    expect(
      screen.getByLabelText('Sélectionner le champ de tri'),
    ).toBeInTheDocument();
  });

  it('should not display sort when showSort is false', () => {
    render(<TrackListContainer showSort={false} />);
    expect(
      screen.queryByLabelText('Sélectionner le champ de tri'),
    ).not.toBeInTheDocument();
  });

  it('should display view toggle when showViewToggle is true', () => {
    render(<TrackListContainer showViewToggle={true} />);
    expect(screen.getByLabelText('Vue liste')).toBeInTheDocument();
    expect(screen.getByLabelText('Vue grille')).toBeInTheDocument();
  });

  it('should not display view toggle when showViewToggle is false', () => {
    render(<TrackListContainer showViewToggle={false} />);
    expect(screen.queryByLabelText('Vue liste')).not.toBeInTheDocument();
  });

  it('should display list view by default', () => {
    render(<TrackListContainer />);
    // TrackList devrait être rendu (on peut vérifier via les tracks)
    expect(mockUseTrackList).toHaveBeenCalled();
  });

  it('should switch to grid view when displayMode changes', () => {
    const { rerender } = render(<TrackListContainer />);

    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      displayMode: 'grid',
    });

    rerender(<TrackListContainer />);

    expect(mockUseTrackList).toHaveBeenCalled();
  });

  it('should display pagination when showPagination is true and totalPages > 1', () => {
    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      totalPages: 3,
    });

    render(<TrackListContainer showPagination={true} />);
    expect(
      screen.getByRole('navigation', { name: /pagination/i }),
    ).toBeInTheDocument();
  });

  it('should not display pagination when showPagination is false', () => {
    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      totalPages: 3,
    });

    render(<TrackListContainer showPagination={false} />);
    expect(
      screen.queryByRole('navigation', { name: /pagination/i }),
    ).not.toBeInTheDocument();
  });

  it('should not display pagination when totalPages <= 1', () => {
    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      totalPages: 1,
    });

    render(<TrackListContainer showPagination={true} />);
    expect(
      screen.queryByRole('navigation', { name: /pagination/i }),
    ).not.toBeInTheDocument();
  });

  it('should call onTrackClick when track is clicked', async () => {
    const mockOnTrackClick = vi.fn();
    render(<TrackListContainer onTrackClick={mockOnTrackClick} />);

    // Le clic sera géré par TrackList/TrackGrid
    // On vérifie que le callback est passé
    expect(mockOnTrackClick).toBeDefined();
  });

  it('should call onTrackPlay when track play button is clicked', async () => {
    const mockOnTrackPlay = vi.fn();
    render(<TrackListContainer onTrackPlay={mockOnTrackPlay} />);

    // Le clic sera géré par TrackList/TrackGrid
    // On vérifie que le callback est passé
    expect(mockOnTrackPlay).toBeDefined();
  });

  it('should handle filter changes', async () => {
    const mockSetFilterOptions = vi.fn();
    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      setFilterOptions: mockSetFilterOptions,
    });

    render(<TrackListContainer showFilters={true} />);

    // Les changements de filtres seront gérés par TrackFilters
    expect(mockSetFilterOptions).toBeDefined();
  });

  it('should handle sort changes', async () => {
    const mockSetSortField = vi.fn();
    const mockSetSortOrder = vi.fn();
    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      setSortField: mockSetSortField,
      setSortOrder: mockSetSortOrder,
    });

    render(<TrackListContainer showSort={true} />);

    // Les changements de tri seront gérés par TrackSort
    expect(mockSetSortField).toBeDefined();
    expect(mockSetSortOrder).toBeDefined();
  });

  it('should handle view mode changes', async () => {
    const mockSetDisplayMode = vi.fn();
    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      setDisplayMode: mockSetDisplayMode,
    });

    render(<TrackListContainer showViewToggle={true} />);

    // Les changements de vue seront gérés par ViewToggle
    expect(mockSetDisplayMode).toBeDefined();
  });

  it('should display error state when error occurs', () => {
    const mockRefreshTracks = vi.fn();
    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      error: new Error('Test error'),
      filteredTracks: [],
      isLoading: false,
      refreshTracks: mockRefreshTracks,
    });

    render(<TrackListContainer />);

    const errorMessages = screen.getAllByText('Erreur lors du chargement');
    expect(errorMessages.length).toBeGreaterThan(0);
    expect(screen.getByLabelText('Réessayer')).toBeInTheDocument();
  });

  it('should call refreshTracks when retry button is clicked', async () => {
    const user = userEvent.setup();
    const mockRefreshTracks = vi.fn();
    mockUseTrackList.mockReturnValue({
      ...defaultTrackListReturn,
      error: new Error('Test error'),
      filteredTracks: [],
      isLoading: false,
      refreshTracks: mockRefreshTracks,
    });

    render(<TrackListContainer />);

    const retryButton = screen.getByLabelText('Réessayer');
    await user.click(retryButton);

    expect(mockRefreshTracks).toHaveBeenCalledTimes(1);
  });

  it('should handle track selection when showSelection is true', () => {
    render(<TrackListContainer showSelection={true} />);

    // La sélection sera gérée par TrackList
    expect(mockUseTrackList).toHaveBeenCalled();
  });

  it('should pass available genres to filters', () => {
    const availableGenres = ['Rock', 'Pop', 'Jazz'];
    render(
      <TrackListContainer
        showFilters={true}
        availableGenres={availableGenres}
      />,
    );

    // Les genres disponibles seront passés à TrackFilters
    expect(mockUseTrackList).toHaveBeenCalled();
  });

  it('should pass available artists to filters', () => {
    const availableArtists = ['Artist 1', 'Artist 2'];
    render(
      <TrackListContainer
        showFilters={true}
        availableArtists={availableArtists}
      />,
    );

    // Les artistes disponibles seront passés à TrackFilters
    expect(mockUseTrackList).toHaveBeenCalled();
  });

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

  it('should use useTrackList with correct options', () => {
    render(
      <TrackListContainer
        useService={false}
        autoLoad={false}
        persistFilters={true}
        persistSort={true}
        syncUrlParams={true}
        storageKeyPrefix="customPrefix"
      />,
    );

    expect(mockUseTrackList).toHaveBeenCalledWith(
      expect.objectContaining({
        useService: false,
        autoLoad: false,
        persistFilters: true,
        persistSort: true,
        syncUrlParams: true,
        storageKeyPrefix: 'customPrefix',
      }),
    );
  });
});
