import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TrackDetailPage } from './TrackDetailPage';
import { getTrack } from '../services/trackService';
import { TrackServiceError as TrackUploadError } from '../errors/trackErrors';
import { usePlayerStore } from '@/stores/player';
import { useToast } from '@/hooks/useToast';
import { useParams, useNavigate } from 'react-router-dom';
import type { Track } from '../types/track';

// Mock dependencies
vi.mock('../services/trackService');
vi.mock('@/stores/player');
vi.mock('@/hooks/useToast');
vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    useParams: vi.fn(),
    useNavigate: vi.fn(),
  };
});

describe('TrackDetailPage', () => {
  const mockNavigate = vi.fn();
  const mockToast = {
    success: vi.fn(),
    error: vi.fn(),
    warning: vi.fn(),
    info: vi.fn(),
    toast: vi.fn(),
  };

  const mockTrack: Track = {
    id: 1,
    creator_id: 123,
    title: 'Test Track',
    artist: 'Test Artist',
    album: 'Test Album',
    duration: 180,
    genre: 'Rock',
    year: 2024,
    file_path: '/uploads/track.mp3',
    file_size: 1024,
    format: 'MP3',
    bitrate: 320,
    sample_rate: 44100,
    waveform_path: '/waveforms/track.png',
    cover_art_path: '/covers/track.jpg',
    is_public: true,
    play_count: 100,
    like_count: 50,
    created_at: '2024-01-01T00:00:00Z',
    updated_at: '2024-01-01T00:00:00Z',
  };

  const mockPlayerStore = {
    play: vi.fn(),
    pause: vi.fn(),
    currentTrack: null,
    isPlaying: false,
    addToQueue: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(useParams).mockReturnValue({ id: '1' });
    vi.mocked(useNavigate).mockReturnValue(mockNavigate);
    vi.mocked(useToast).mockReturnValue(mockToast);
    vi.mocked(usePlayerStore).mockReturnValue(mockPlayerStore as any);
  });

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

  it('should render loading state initially', async () => {
    vi.mocked(getTrack).mockImplementation(
      () =>
        new Promise((resolve) => {
          setTimeout(() => resolve(mockTrack), 100);
        }),
    );

    render(<TrackDetailPage />);

    expect(screen.getByText(/chargement du track/i)).toBeInTheDocument();
  });

  it('should render track details after loading', async () => {
    vi.mocked(getTrack).mockResolvedValue(mockTrack);

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText('Test Track')).toBeInTheDocument();
      expect(screen.getByText('Test Artist')).toBeInTheDocument();
      expect(screen.getByText('Test Album')).toBeInTheDocument();
    });

    expect(screen.getByText('3:00')).toBeInTheDocument(); // 180 seconds
    expect(screen.getByText('Rock')).toBeInTheDocument();
    expect(screen.getByText('2024')).toBeInTheDocument();
    expect(screen.getByText('100')).toBeInTheDocument(); // play_count
    expect(screen.getByText('50')).toBeInTheDocument(); // like_count
  });

  it('should display error message on load failure', async () => {
    const error = new TrackUploadError('Track not found', 'VALIDATION', false);
    vi.mocked(getTrack).mockRejectedValue(error);

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText(/track introuvable/i)).toBeInTheDocument();
      expect(mockToast.error).toHaveBeenCalledWith('Track not found');
    });
  });

  it('should handle missing track ID', async () => {
    vi.mocked(useParams).mockReturnValue({ id: undefined });

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText(/track id is required/i)).toBeInTheDocument();
    });
  });

  it('should play track when play button is clicked', async () => {
    vi.mocked(getTrack).mockResolvedValue(mockTrack);

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText('Test Track')).toBeInTheDocument();
    });

    const playButton = screen.getByRole('button', { name: /play/i });
    await userEvent.click(playButton);

    expect(mockPlayerStore.play).toHaveBeenCalled();
  });

  it('should pause track when pause button is clicked if track is playing', async () => {
    vi.mocked(getTrack).mockResolvedValue(mockTrack);
    vi.mocked(usePlayerStore).mockReturnValue({
      ...mockPlayerStore,
      currentTrack: { id: '1' } as any,
      isPlaying: true,
    } as any);

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText('Test Track')).toBeInTheDocument();
    });

    const pauseButton = screen.getByRole('button', { name: /pause/i });
    await userEvent.click(pauseButton);

    expect(mockPlayerStore.pause).toHaveBeenCalled();
  });

  it('should add track to queue when queue button is clicked', async () => {
    vi.mocked(getTrack).mockResolvedValue(mockTrack);

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText('Test Track')).toBeInTheDocument();
    });

    const queueButtons = screen.getAllByRole('button');
    const queueButton = queueButtons.find((btn) =>
      btn.getAttribute('title')?.includes("file d'attente"),
    );

    if (queueButton) {
      await userEvent.click(queueButton);
      expect(mockPlayerStore.addToQueue).toHaveBeenCalled();
      expect(mockToast.success).toHaveBeenCalledWith(
        expect.stringContaining('ajouté à la file'),
      );
    }
  });

  it('should copy share link when share button is clicked', async () => {
    vi.mocked(getTrack).mockResolvedValue(mockTrack);
    const mockWriteText = vi.fn().mockResolvedValue(undefined);
    Object.assign(navigator, {
      clipboard: {
        writeText: mockWriteText,
      },
    });

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText('Test Track')).toBeInTheDocument();
    });

    const shareButtons = screen.getAllByRole('button');
    const shareButton = shareButtons.find((btn) =>
      btn.getAttribute('title')?.includes('Partager'),
    );

    if (shareButton) {
      await userEvent.click(shareButton);
      expect(mockWriteText).toHaveBeenCalled();
      expect(mockToast.success).toHaveBeenCalledWith(
        expect.stringContaining('copié'),
      );
    }
  });

  it('should navigate back when back button is clicked', async () => {
    vi.mocked(getTrack).mockResolvedValue(mockTrack);

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText('Test Track')).toBeInTheDocument();
    });

    const backButton = screen.getByRole('button', { name: /retour/i });
    await userEvent.click(backButton);

    expect(mockNavigate).toHaveBeenCalledWith(-1);
  });

  it('should display waveform if available', async () => {
    vi.mocked(getTrack).mockResolvedValue(mockTrack);

    render(<TrackDetailPage />);

    await waitFor(() => {
      const waveform = screen.getByAltText('Waveform');
      expect(waveform).toBeInTheDocument();
      expect(waveform).toHaveAttribute('src', '/waveforms/track.png');
    });
  });

  it('should display cover art if available', async () => {
    vi.mocked(getTrack).mockResolvedValue(mockTrack);

    render(<TrackDetailPage />);

    await waitFor(() => {
      const coverArt = screen.getByAltText('Test Track');
      expect(coverArt).toBeInTheDocument();
      expect(coverArt).toHaveAttribute('src', '/covers/track.jpg');
    });
  });

  it('should display placeholder if no cover art', async () => {
    const trackWithoutCover = { ...mockTrack, cover_art_path: undefined };
    vi.mocked(getTrack).mockResolvedValue(trackWithoutCover);

    render(<TrackDetailPage />);

    await waitFor(() => {
      expect(screen.getByText('Test Track')).toBeInTheDocument();
    });

    // Should have Music icon placeholder
    const musicIcons = screen.getAllByRole('img', { hidden: true });
    expect(musicIcons.length).toBeGreaterThan(0);
  });
});
