import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';
import { AudioPlayer } from './AudioPlayer';
import { usePlayer } from '../hooks/usePlayer';

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

describe('AudioPlayer', () => {
  const mockPlayer = {
    currentTrack: null,
    isPlaying: false,
    currentTime: 0,
    duration: 0,
    volume: 100,
    muted: false,
    queue: [],
    currentIndex: -1,
    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(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(usePlayer).mockReturnValue(mockPlayer);
  });

  it('should render audio player', () => {
    render(<AudioPlayer />);

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

  it('should render audio element', () => {
    render(<AudioPlayer />);

    const audioElement = screen.getByTestId('audio-element');
    expect(audioElement).toBeInTheDocument();
    expect(audioElement.tagName).toBe('AUDIO');
  });

  it('should pass audio ref to usePlayer hook', () => {
    render(<AudioPlayer />);

    // usePlayer should be called with a ref object
    expect(usePlayer).toHaveBeenCalled();
    const callArgs = vi.mocked(usePlayer).mock.calls[0][0];
    expect(callArgs).toBeDefined();
    expect(callArgs?.current).toBeInstanceOf(HTMLAudioElement);
  });

  it('should display no track message when no track is selected', () => {
    render(<AudioPlayer />);

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

  it('should display current track when track is playing', () => {
    const track = {
      id: 1,
      title: 'Test Track',
      duration: 180,
      url: 'https://example.com/track.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track,
    });

    render(<AudioPlayer />);

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

  it('should display artist when available', () => {
    const track = {
      id: 1,
      title: 'Test Track',
      artist: 'Test Artist',
      duration: 180,
      url: 'https://example.com/track.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track,
    });

    render(<AudioPlayer />);

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

  it('should apply custom className', () => {
    const { container } = render(<AudioPlayer className="custom-class" />);

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

  it('should set preload attribute on audio element', () => {
    render(<AudioPlayer preload="auto" />);

    const audioElement = screen.getByTestId(
      'audio-element',
    ) as HTMLAudioElement;
    expect(audioElement.preload).toBe('auto');
  });

  it('should set autoplay attribute on audio element', () => {
    render(<AudioPlayer autoPlay={true} />);

    const audioElement = screen.getByTestId(
      'audio-element',
    ) as HTMLAudioElement;
    expect(audioElement.autoplay).toBe(true);
  });

  it('should have default preload value', () => {
    render(<AudioPlayer />);

    const audioElement = screen.getByTestId(
      'audio-element',
    ) as HTMLAudioElement;
    expect(audioElement.preload).toBe('metadata');
  });

  it('should have default autoplay value', () => {
    render(<AudioPlayer />);

    const audioElement = screen.getByTestId(
      'audio-element',
    ) as HTMLAudioElement;
    expect(audioElement.autoplay).toBe(false);
  });

  it('should hide audio element visually', () => {
    render(<AudioPlayer />);

    const audioElement = screen.getByTestId('audio-element');
    expect(audioElement).toHaveStyle({ display: 'none' });
  });

  it('should handle queue with multiple tracks', () => {
    const track1 = {
      id: 1,
      title: 'Track 1',
      duration: 180,
      url: 'https://example.com/track1.mp3',
    };
    const track2 = {
      id: 2,
      title: 'Track 2',
      duration: 200,
      url: 'https://example.com/track2.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track1,
      queue: [track1, track2],
      currentIndex: 0,
    });

    render(<AudioPlayer />);

    expect(screen.getByText('Track 1')).toBeInTheDocument();
  });

  it('should handle compact mode', () => {
    const track = {
      id: 1,
      title: 'Test Track',
      duration: 180,
      url: 'https://example.com/track.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track,
    });

    render(<AudioPlayer compact={true} />);

    expect(screen.getByTestId('audio-player')).toBeInTheDocument();
    expect(screen.getByText('Test Track')).toBeInTheDocument();
  });

  it('should handle error state', () => {
    const track = {
      id: 1,
      title: 'Test Track',
      duration: 180,
      url: 'https://example.com/track.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track,
    });

    // Mock error state - this would be set by the component internally
    render(<AudioPlayer />);

    // Error component should be renderable
    expect(screen.getByTestId('audio-player')).toBeInTheDocument();
  });

  it('should handle loading state', () => {
    const track = {
      id: 1,
      title: 'Test Track',
      duration: 180,
      url: 'https://example.com/track.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track,
    });

    render(<AudioPlayer />);

    // Loading component should be renderable
    expect(screen.getByTestId('audio-player')).toBeInTheDocument();
  });

  it('should handle queue navigation with next button', () => {
    const track1 = {
      id: 1,
      title: 'Track 1',
      duration: 180,
      url: 'https://example.com/track1.mp3',
    };
    const track2 = {
      id: 2,
      title: 'Track 2',
      duration: 200,
      url: 'https://example.com/track2.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track1,
      queue: [track1, track2],
      currentIndex: 0,
    });

    render(<AudioPlayer />);

    // Next button should be enabled when there are more tracks
    expect(mockPlayer.next).toBeDefined();
  });

  it('should handle queue navigation with previous button', () => {
    const track1 = {
      id: 1,
      title: 'Track 1',
      duration: 180,
      url: 'https://example.com/track1.mp3',
    };
    const track2 = {
      id: 2,
      title: 'Track 2',
      duration: 200,
      url: 'https://example.com/track2.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track2,
      queue: [track1, track2],
      currentIndex: 1,
    });

    render(<AudioPlayer />);

    // Previous button should be enabled when not at first track
    expect(mockPlayer.previous).toBeDefined();
  });

  it('should disable next button when at end of queue', () => {
    const track = {
      id: 1,
      title: 'Track 1',
      duration: 180,
      url: 'https://example.com/track1.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track,
      queue: [track],
      currentIndex: 0,
    });

    render(<AudioPlayer />);

    // Next should not be available when at end
    expect(mockPlayer.next).toBeDefined();
  });

  it('should disable previous button when at start of queue', () => {
    const track = {
      id: 1,
      title: 'Track 1',
      duration: 180,
      url: 'https://example.com/track1.mp3',
    };

    vi.mocked(usePlayer).mockReturnValue({
      ...mockPlayer,
      currentTrack: track,
      queue: [track],
      currentIndex: 0,
    });

    render(<AudioPlayer />);

    // Previous should not be available when at start
    expect(mockPlayer.previous).toBeDefined();
  });
});
