/**
 * Tests end-to-end pour le player complet
 * Vérifie l'intégration de tous les composants
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { AudioPlayer } from '../components/AudioPlayer';
import { usePlayer } from '../hooks/usePlayer';
import { useKeyboardShortcuts } from '../hooks/useKeyboardShortcuts';
import type { Track } from '../types';

// Mock usePlayer
vi.mock('../hooks/usePlayer', () => ({
  usePlayer: vi.fn(),
}));

// Mock useKeyboardShortcuts
vi.mock('../hooks/useKeyboardShortcuts', () => ({
  useKeyboardShortcuts: vi.fn(),
}));

const mockTrack: Track = {
  id: 1,
  title: 'Test Track',
  artist: 'Test Artist',
  album: 'Test Album',
  duration: 180,
  url: 'https://example.com/track.mp3',
  cover: 'https://example.com/cover.jpg',
  genre: 'Rock',
};

const defaultPlayerState = {
  currentTrack: mockTrack,
  isPlaying: false,
  currentTime: 30,
  duration: 180,
  volume: 100,
  muted: false,
  queue: [mockTrack],
  currentIndex: 0,
  repeat: 'off' as const,
  shuffle: false,
  play: vi.fn(),
  pause: vi.fn(),
  resume: vi.fn(),
  stop: vi.fn(),
  next: vi.fn(),
  previous: vi.fn(),
  seek: vi.fn(),
  setVolume: vi.fn(),
  toggleMute: vi.fn(),
  toggleShuffle: vi.fn(),
  setRepeat: vi.fn(),
  addToQueue: vi.fn(),
  clearQueue: vi.fn(),
};

describe('AudioPlayer E2E Tests', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(usePlayer).mockReturnValue(defaultPlayerState as any);
    vi.mocked(useKeyboardShortcuts).mockImplementation(() => {});
  });

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

  it('should render complete player with all components', () => {
    render(<AudioPlayer />);

    expect(screen.getByTestId('audio-player')).toBeInTheDocument();
    expect(screen.getByTestId('audio-element')).toBeInTheDocument();
  });

  it('should display track information when track is playing', () => {
    render(<AudioPlayer />);

    expect(screen.getByText('Test Track')).toBeInTheDocument();
    expect(screen.getByText('Test Artist')).toBeInTheDocument();
  });

  it('should display play/pause button', () => {
    render(<AudioPlayer />);

    const playButton = screen.getByLabelText('Lire');
    expect(playButton).toBeInTheDocument();
  });

  it('should play track when play button is clicked', async () => {
    const user = userEvent.setup();
    const mockResume = vi.fn();
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      resume: mockResume,
    } as any);

    render(<AudioPlayer />);

    const playButton = screen.getByLabelText('Lire');
    await user.click(playButton);

    expect(mockResume).toHaveBeenCalled();
  });

  it('should pause track when pause button is clicked', async () => {
    const user = userEvent.setup();
    const mockPause = vi.fn();
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      isPlaying: true,
      pause: mockPause,
    } as any);

    render(<AudioPlayer />);

    const pauseButton = screen.getByLabelText('Mettre en pause');
    await user.click(pauseButton);

    expect(mockPause).toHaveBeenCalled();
  });

  it('should display progress bar', () => {
    render(<AudioPlayer />);

    // Il peut y avoir plusieurs sliders (progress bar et volume), donc on cherche tous les sliders
    const sliders = screen.getAllByRole('slider');
    // Au moins un slider devrait être présent (progress bar ou volume)
    expect(sliders.length).toBeGreaterThan(0);
  });

  it('should display time display', () => {
    render(<AudioPlayer />);

    expect(screen.getByRole('timer')).toBeInTheDocument();
    expect(screen.getByText('0:30')).toBeInTheDocument();
    expect(screen.getByText('3:00')).toBeInTheDocument();
  });

  it('should seek when progress bar is clicked', async () => {
    const user = userEvent.setup();
    const mockSeek = vi.fn();
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      seek: mockSeek,
    } as any);

    render(<AudioPlayer />);

    // Chercher tous les sliders et prendre le premier (qui devrait être la progress bar)
    const sliders = screen.getAllByRole('slider');
    // Le premier slider devrait être la progress bar
    if (sliders.length > 0) {
      const progressBar = sliders[0];

      // Simuler un clic sur la barre de progression
      await act(async () => {
        await user.click(progressBar);
      });
    }

    // Le seek devrait être disponible
    expect(mockSeek).toBeDefined();
  });

  it('should display next/previous buttons', () => {
    render(<AudioPlayer />);

    // Il y a deux sets de boutons next/previous, donc on utilise getAllByLabelText
    const nextButtons = screen.getAllByLabelText('Piste suivante');
    const previousButtons = screen.getAllByLabelText('Piste précédente');
    expect(nextButtons.length).toBeGreaterThan(0);
    expect(previousButtons.length).toBeGreaterThan(0);
  });

  it('should call next when next button is clicked', async () => {
    const user = userEvent.setup();
    const mockNext = vi.fn();
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      next: mockNext,
      queue: [mockTrack, { ...mockTrack, id: 2 }],
      currentIndex: 0,
    } as any);

    render(<AudioPlayer />);

    // Prendre le premier bouton next
    const nextButtons = screen.getAllByLabelText('Piste suivante');
    await user.click(nextButtons[0]);

    expect(mockNext).toHaveBeenCalled();
  });

  it('should display volume control', () => {
    render(<AudioPlayer />);

    // Le volume control a un bouton mute/unmute avec aria-label "Volume: X%" ou "Volume muet"
    const volumeButtons = screen.queryAllByLabelText(/Volume/);
    expect(volumeButtons.length).toBeGreaterThan(0);
  });

  it('should display repeat/shuffle buttons', () => {
    render(<AudioPlayer />);

    // Il y a deux boutons (repeat et shuffle), donc on utilise getAllByLabelText
    const buttons = screen.getAllByLabelText(/Répéter|Mélange/);
    expect(buttons.length).toBeGreaterThan(0);
  });

  it('should display quality selector when enabled', () => {
    render(<AudioPlayer showQualitySelector={true} />);

    expect(screen.getByLabelText(/Qualité audio/)).toBeInTheDocument();
  });

  it('should not display quality selector when disabled', () => {
    render(<AudioPlayer showQualitySelector={false} />);

    expect(screen.queryByLabelText(/Qualité audio/)).not.toBeInTheDocument();
  });

  it('should display speed control when enabled', () => {
    render(<AudioPlayer showSpeedControl={true} />);

    expect(screen.getByLabelText(/Vitesse de lecture/)).toBeInTheDocument();
  });

  it('should not display speed control when disabled', () => {
    render(<AudioPlayer showSpeedControl={false} />);

    expect(
      screen.queryByLabelText(/Vitesse de lecture/),
    ).not.toBeInTheDocument();
  });

  it('should render compact version when compact prop is true', () => {
    render(<AudioPlayer compact={true} />);

    expect(screen.getByTestId('audio-player')).toBeInTheDocument();
    // En mode compact, certains éléments peuvent être masqués
    expect(screen.getByText('Test Track')).toBeInTheDocument();
  });

  it('should display empty state when no track is selected', () => {
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      currentTrack: null,
    } as any);

    render(<AudioPlayer />);

    expect(screen.getByText('Aucune piste sélectionnée')).toBeInTheDocument();
  });

  it('should display error when error occurs', async () => {
    render(<AudioPlayer />);

    // Simuler une erreur en déclenchant l'événement error sur l'élément audio
    const audioElement = screen.getByTestId('audio-element');
    act(() => {
      const errorEvent = new Event('error');
      audioElement.dispatchEvent(errorEvent);
    });

    // Attendre que l'erreur soit affichée
    await waitFor(() => {
      expect(screen.queryByRole('alert')).toBeInTheDocument();
    });
  });

  it('should enable keyboard shortcuts', () => {
    render(<AudioPlayer />);

    expect(useKeyboardShortcuts).toHaveBeenCalledWith(
      expect.any(Object),
      expect.objectContaining({
        enabled: true,
        seekStep: 5,
        volumeStep: 5,
      }),
    );
  });

  it('should apply custom className', () => {
    const { container } = render(<AudioPlayer className="custom-class" />);
    const player = container.querySelector('[data-testid="audio-player"]');
    expect(player).toHaveClass('custom-class');
  });
});
