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

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

// Mock child components
vi.mock('./TrackInfo', () => ({
  TrackInfo: ({ track }: { track: Track | null }) => (
    <div data-testid="track-info">{track?.title || 'No track'}</div>
  ),
}));

vi.mock('./PlayPauseButton', () => ({
  PlayPauseButton: ({
    isPlaying,
    onClick,
  }: {
    isPlaying: boolean;
    onClick: () => void;
  }) => (
    <button data-testid="play-pause" onClick={onClick}>
      {isPlaying ? 'Pause' : 'Play'}
    </button>
  ),
}));

vi.mock('./NextPreviousButtons', () => ({
  NextPreviousButtons: ({
    onNext,
    onPrevious,
    canGoNext,
    canGoPrevious,
  }: {
    onNext: () => void;
    onPrevious: () => void;
    canGoNext: boolean;
    canGoPrevious: boolean;
  }) => (
    <div data-testid="next-previous">
      <button onClick={onPrevious} disabled={!canGoPrevious}>
        Previous
      </button>
      <button onClick={onNext} disabled={!canGoNext}>
        Next
      </button>
    </div>
  ),
}));

vi.mock('./ProgressBar', () => ({
  ProgressBar: ({
    currentTime,
    duration,
    onSeek,
  }: {
    currentTime: number;
    duration: number;
    onSeek: (time: number) => void;
  }) => (
    <div data-testid="progress-bar" onClick={() => onSeek(30)}>
      {currentTime} / {duration}
    </div>
  ),
}));

vi.mock('./VolumeControl', () => ({
  VolumeControl: ({
    volume,
    muted,
    onVolumeChange,
    onMuteToggle,
  }: {
    volume: number;
    muted: boolean;
    onVolumeChange: (vol: number) => void;
    onMuteToggle: () => void;
  }) => (
    <div data-testid="volume-control">
      <button onClick={onMuteToggle}>{muted ? 'Unmute' : 'Mute'}</button>
      <button onClick={() => onVolumeChange(50)}>Set Volume</button>
    </div>
  ),
}));

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

const defaultPlayerState = {
  currentTrack: mockTrack,
  isPlaying: false,
  currentTime: 0,
  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('MiniPlayer', () => {
  const mockOnToggle = vi.fn();
  const mockOnClose = vi.fn();

  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(usePlayer).mockReturnValue(defaultPlayerState as any);
  });

  it('should not render when isVisible is false', () => {
    render(<MiniPlayer isVisible={false} onToggle={mockOnToggle} />);
    expect(
      screen.queryByRole('region', { name: 'Mini lecteur audio' }),
    ).not.toBeInTheDocument();
  });

  it('should not render when no track is playing', () => {
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      currentTrack: null,
    } as any);

    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    expect(
      screen.queryByRole('region', { name: 'Mini lecteur audio' }),
    ).not.toBeInTheDocument();
  });

  it('should render when visible and track is playing', () => {
    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    expect(
      screen.getByRole('region', { name: 'Mini lecteur audio' }),
    ).toBeInTheDocument();
  });

  it('should display track info', () => {
    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    expect(screen.getByTestId('track-info')).toBeInTheDocument();
    expect(screen.getByText('Test Track')).toBeInTheDocument();
  });

  it('should display play/pause button', () => {
    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    expect(screen.getByTestId('play-pause')).toBeInTheDocument();
  });

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

    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const playPauseButton = screen.getByTestId('play-pause');
    await user.click(playPauseButton);

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

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

    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const playPauseButton = screen.getByTestId('play-pause');
    await user.click(playPauseButton);

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

  it('should display next/previous buttons', () => {
    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    expect(screen.getByTestId('next-previous')).toBeInTheDocument();
  });

  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(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const nextButton = screen.getByText('Next');
    await user.click(nextButton);

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

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

    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const previousButton = screen.getByText('Previous');
    await user.click(previousButton);

    expect(mockPrevious).toHaveBeenCalled();
  });

  it('should display progress bar', () => {
    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    expect(screen.getByTestId('progress-bar')).toBeInTheDocument();
  });

  it('should call 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(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const progressBar = screen.getByTestId('progress-bar');
    await user.click(progressBar);

    expect(mockSeek).toHaveBeenCalledWith(30);
  });

  it('should display volume control', () => {
    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    expect(screen.getByTestId('volume-control')).toBeInTheDocument();
  });

  it('should call toggle when toggle button is clicked', async () => {
    const user = userEvent.setup();
    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);

    const toggleButton = screen.getByLabelText('Agrandir le lecteur');
    await user.click(toggleButton);

    expect(mockOnToggle).toHaveBeenCalled();
  });

  it('should display close button when onClose is provided', () => {
    render(
      <MiniPlayer
        isVisible={true}
        onToggle={mockOnToggle}
        onClose={mockOnClose}
      />,
    );
    expect(screen.getByLabelText('Fermer le mini lecteur')).toBeInTheDocument();
  });

  it('should not display close button when onClose is not provided', () => {
    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    expect(
      screen.queryByLabelText('Fermer le mini lecteur'),
    ).not.toBeInTheDocument();
  });

  it('should call onClose when close button is clicked', async () => {
    const user = userEvent.setup();
    render(
      <MiniPlayer
        isVisible={true}
        onToggle={mockOnToggle}
        onClose={mockOnClose}
      />,
    );

    const closeButton = screen.getByLabelText('Fermer le mini lecteur');
    await user.click(closeButton);

    expect(mockOnClose).toHaveBeenCalled();
  });

  it('should have fixed position at bottom by default', () => {
    const { container } = render(
      <MiniPlayer isVisible={true} onToggle={mockOnToggle} />,
    );
    const miniPlayer = container.querySelector('[role="region"]');
    expect(miniPlayer).toHaveClass('fixed', 'bottom-0');
  });

  it('should have fixed position at top when position prop is top', () => {
    const { container } = render(
      <MiniPlayer isVisible={true} onToggle={mockOnToggle} position="top" />,
    );
    const miniPlayer = container.querySelector('[role="region"]');
    expect(miniPlayer).toHaveClass('fixed', 'top-0');
    expect(miniPlayer).not.toHaveClass('bottom-0');
  });

  it('should apply custom className', () => {
    const { container } = render(
      <MiniPlayer
        isVisible={true}
        onToggle={mockOnToggle}
        className="custom-class"
      />,
    );
    const miniPlayer = container.querySelector('[role="region"]');
    expect(miniPlayer).toHaveClass('custom-class');
  });

  it('should disable next button when cannot go next', () => {
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      queue: [mockTrack],
      currentIndex: 0,
    } as any);

    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const nextButton = screen.getByText('Next');
    expect(nextButton).toBeDisabled();
  });

  it('should disable previous button when cannot go previous', () => {
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      queue: [mockTrack],
      currentIndex: 0,
    } as any);

    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const previousButton = screen.getByText('Previous');
    expect(previousButton).toBeDisabled();
  });

  it('should enable next button when can go next', () => {
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      queue: [mockTrack, { ...mockTrack, id: 2 }],
      currentIndex: 0,
    } as any);

    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const nextButton = screen.getByText('Next');
    expect(nextButton).not.toBeDisabled();
  });

  it('should enable previous button when can go previous', () => {
    vi.mocked(usePlayer).mockReturnValue({
      ...defaultPlayerState,
      queue: [mockTrack, { ...mockTrack, id: 2 }],
      currentIndex: 1,
    } as any);

    render(<MiniPlayer isVisible={true} onToggle={mockOnToggle} />);
    const previousButton = screen.getByText('Previous');
    expect(previousButton).not.toBeDisabled();
  });
});
