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

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',
};

describe('TrackCard', () => {
  const mockOnPlay = vi.fn();
  const mockOnLike = vi.fn();
  const mockOnMore = vi.fn();
  const mockOnClick = vi.fn();

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

  it('should render track card', () => {
    render(<TrackCard track={mockTrack} />);
    expect(screen.getByText('Test Track')).toBeInTheDocument();
  });

  it('should display track title', () => {
    render(<TrackCard track={mockTrack} />);
    expect(screen.getByText('Test Track')).toBeInTheDocument();
  });

  it('should display track artist', () => {
    render(<TrackCard track={mockTrack} />);
    expect(screen.getByText('Test Artist')).toBeInTheDocument();
  });

  it('should display cover image', () => {
    render(<TrackCard track={mockTrack} />);
    const image = screen.getByAltText('Cover de Test Track');
    expect(image).toBeInTheDocument();
    expect(image).toHaveAttribute('src', mockTrack.cover);
  });

  it('should display placeholder when cover is missing', () => {
    const trackWithoutCover = { ...mockTrack, cover: undefined };
    const { container } = render(<TrackCard track={trackWithoutCover} />);
    // Should show Music icon placeholder (lucide-react icons have aria-hidden="true")
    const musicIcon = container.querySelector('svg');
    expect(musicIcon).toBeInTheDocument();
  });

  it('should display placeholder when cover image fails to load', async () => {
    render(<TrackCard track={mockTrack} />);
    const image = screen.getByAltText('Cover de Test Track');

    // Simulate image error
    const errorEvent = new Event('error');
    image.dispatchEvent(errorEvent);

    await waitFor(() => {
      // After error, should show placeholder
      expect(
        screen.queryByAltText('Cover de Test Track'),
      ).not.toBeInTheDocument();
    });
  });

  it('should display duration when showDuration is true', () => {
    render(<TrackCard track={mockTrack} showDuration={true} />);
    expect(screen.getByText('3:00')).toBeInTheDocument();
  });

  it('should not display duration when showDuration is false', () => {
    render(<TrackCard track={mockTrack} showDuration={false} />);
    expect(screen.queryByText('3:00')).not.toBeInTheDocument();
  });

  it('should call onPlay when play button is clicked', async () => {
    const user = userEvent.setup();
    render(<TrackCard track={mockTrack} onPlay={mockOnPlay} />);

    const card = screen.getByRole('button', { name: /Piste:/ });
    await user.hover(card);

    await waitFor(() => {
      const playButton = screen.getByLabelText('Lire Test Track');
      expect(playButton).toBeInTheDocument();
    });

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

    expect(mockOnPlay).toHaveBeenCalledWith(mockTrack);
  });

  it('should call onLike when like button is clicked', async () => {
    const user = userEvent.setup();
    render(<TrackCard track={mockTrack} onLike={mockOnLike} />);

    const likeButton = screen.getByLabelText(/Ajouter.*favoris/);
    await user.click(likeButton);

    expect(mockOnLike).toHaveBeenCalledWith(mockTrack);
  });

  it('should call onMore when more button is clicked', async () => {
    const user = userEvent.setup();
    render(<TrackCard track={mockTrack} onMore={mockOnMore} />);

    const moreButton = screen.getByLabelText(/Plus d'options/);
    await user.click(moreButton);

    expect(mockOnMore).toHaveBeenCalledWith(mockTrack);
  });

  it('should call onClick when card is clicked', async () => {
    const user = userEvent.setup();
    render(<TrackCard track={mockTrack} onClick={mockOnClick} />);

    const card = screen.getByRole('button', { name: /Piste:/ });
    await user.click(card);

    expect(mockOnClick).toHaveBeenCalledWith(mockTrack);
  });

  it('should not call onClick when action button is clicked', async () => {
    const user = userEvent.setup();
    render(
      <TrackCard track={mockTrack} onClick={mockOnClick} onLike={mockOnLike} />,
    );

    const likeButton = screen.getByLabelText(/Ajouter.*favoris/);
    await user.click(likeButton);

    expect(mockOnLike).toHaveBeenCalled();
    expect(mockOnClick).not.toHaveBeenCalled();
  });

  it('should show liked state when isLiked is true', () => {
    render(<TrackCard track={mockTrack} onLike={mockOnLike} isLiked={true} />);

    const likeButton = screen.getByLabelText(/Retirer.*favoris/);
    expect(likeButton).toHaveAttribute('aria-pressed', 'true');
  });

  it('should show playing state when isPlaying is true', () => {
    render(
      <TrackCard track={mockTrack} onPlay={mockOnPlay} isPlaying={true} />,
    );

    const card = screen.getByRole('button', { name: /Piste:/ });
    // Play button should be visible when playing
    expect(card).toBeInTheDocument();
  });

  it('should hide actions when showActions is false', () => {
    render(
      <TrackCard
        track={mockTrack}
        onLike={mockOnLike}
        onMore={mockOnMore}
        showActions={false}
      />,
    );

    expect(screen.queryByLabelText(/favoris/)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(/Plus d'options/)).not.toBeInTheDocument();
  });

  it('should apply custom className', () => {
    const { container } = render(
      <TrackCard track={mockTrack} className="custom-class" />,
    );
    const card = container.querySelector('[role="button"]');
    expect(card).toHaveClass('custom-class');
  });

  it('should support different sizes', () => {
    const { rerender } = render(<TrackCard track={mockTrack} size="sm" />);
    expect(screen.getByText('Test Track')).toBeInTheDocument();

    rerender(<TrackCard track={mockTrack} size="md" />);
    expect(screen.getByText('Test Track')).toBeInTheDocument();

    rerender(<TrackCard track={mockTrack} size="lg" />);
    expect(screen.getByText('Test Track')).toBeInTheDocument();
  });

  it('should have accessible attributes', () => {
    render(<TrackCard track={mockTrack} onClick={mockOnClick} />);

    const card = screen.getByRole('button', { name: /Piste:/ });
    expect(card).toHaveAttribute('tabIndex', '0');
    expect(card).toHaveAttribute('aria-label');
  });

  it('should not be clickable when onClick is not provided', () => {
    render(<TrackCard track={mockTrack} />);

    const card = screen.getByRole('button', { name: /Piste:/ });
    expect(card).toHaveAttribute('tabIndex', '-1');
  });

  it('should handle track without artist', () => {
    const trackWithoutArtist = { ...mockTrack, artist: undefined };
    render(<TrackCard track={trackWithoutArtist} />);

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

  it('should show hover overlay on cover when hovered', async () => {
    const user = userEvent.setup();
    const { container } = render(
      <TrackCard track={mockTrack} onPlay={mockOnPlay} />,
    );

    const card = container.querySelector('[role="button"]') as HTMLElement;
    await user.hover(card);

    // Vérifier que l'overlay est présent
    const overlay = container.querySelector('.bg-gradient-to-t');
    expect(overlay).toBeInTheDocument();
  });

  it('should show play button on hover', async () => {
    const user = userEvent.setup();
    const { container } = render(
      <TrackCard track={mockTrack} onPlay={mockOnPlay} />,
    );

    const card = container.querySelector('[role="button"]') as HTMLElement;
    await user.hover(card);

    await waitFor(() => {
      // Le bouton play devrait être visible au hover
      const playButton = container.querySelector('button[aria-label*="Lire"]');
      expect(playButton).toBeInTheDocument();
    });
  });

  it('should show actions on hover with animation', async () => {
    const user = userEvent.setup();
    const { container } = render(
      <TrackCard track={mockTrack} onLike={mockOnLike} onMore={mockOnMore} />,
    );

    const card = container.querySelector('[role="button"]') as HTMLElement;
    await user.hover(card);

    await waitFor(() => {
      // Les actions devraient être visibles au hover
      const actionsContainer = container.querySelector(
        '.group-hover\\:opacity-100',
      );
      expect(actionsContainer).toBeInTheDocument();
    });
  });

  it('should apply scale animation on hover', () => {
    const { container } = render(<TrackCard track={mockTrack} />);

    const card = container.querySelector('[role="button"]') as HTMLElement;
    expect(card).toHaveClass('hover:scale-[1.02]');
  });

  it('should animate cover image on hover', async () => {
    const user = userEvent.setup();
    const { container } = render(<TrackCard track={mockTrack} />);

    const card = container.querySelector('[role="button"]') as HTMLElement;
    await user.hover(card);

    await waitFor(() => {
      // L'image devrait avoir une classe de scale au hover
      const coverContainer = container.querySelector('.scale-105');
      expect(coverContainer).toBeInTheDocument();
    });
  });

  it('should show playing animation when isPlaying is true', () => {
    const { container } = render(
      <TrackCard track={mockTrack} isPlaying={true} onPlay={mockOnPlay} />,
    );

    // Le bouton play devrait être visible même sans hover
    const playButton = container.querySelector('button[aria-label*="Lire"]');
    expect(playButton).toBeInTheDocument();

    // Devrait avoir l'animation de lecture
    const playingIndicator = container.querySelector('.animate-pulse');
    expect(playingIndicator).toBeInTheDocument();
  });
});
