import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { TrackStatsDisplay } from './TrackStatsDisplay';
import { getTrackStats } from '../services/analyticsService';
import { TrackServiceError as TrackStatsError } from '../errors/trackErrors';

// Mock dependencies
vi.mock('../services/trackStatsService');
vi.mock('@/components/ui/loading-spinner', () => ({
  LoadingSpinner: () => <div data-testid="loading-spinner">Loading...</div>,
}));
vi.mock('@/components/ui/alert', () => ({
  Alert: ({ children, className }: any) => (
    <div className={className}>{children}</div>
  ),
  AlertDescription: ({ children }: any) => <div>{children}</div>,
}));

describe('TrackStatsDisplay', () => {
  const mockStats = {
    views: 1000,
    likes: 250,
    comments: 50,
    total_play_time: 3665, // 1h 1m 5s
    downloads: 25,
  };

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

  it('should render loading state initially', () => {
    vi.mocked(getTrackStats).mockImplementation(
      () => new Promise(() => { }), // Never resolves
    );

    render(<TrackStatsDisplay trackId={123} />);

    expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
  });

  it('should display stats in horizontal layout', async () => {
    vi.mocked(getTrackStats).mockResolvedValue(mockStats);

    render(<TrackStatsDisplay trackId={123} />);

    await waitFor(() => {
      expect(screen.getByText('1.0K')).toBeInTheDocument(); // views
      expect(screen.getByText('250')).toBeInTheDocument(); // likes
      expect(screen.getByText('50')).toBeInTheDocument(); // comments
      expect(screen.getByText('25')).toBeInTheDocument(); // downloads
      expect(screen.getByText('1h 1m')).toBeInTheDocument(); // total_play_time
    });

    // Check icons are present (Eye, Heart, MessageCircle, Download, Clock)
    const icons = screen.getAllByRole('img', { hidden: true });
    expect(icons.length).toBeGreaterThan(0);
  });

  it('should display stats in vertical layout', async () => {
    vi.mocked(getTrackStats).mockResolvedValue(mockStats);

    render(<TrackStatsDisplay trackId={123} variant="vertical" />);

    await waitFor(() => {
      expect(screen.getByText('1.0K')).toBeInTheDocument();
    });
  });

  it('should show labels when showLabels is true', async () => {
    vi.mocked(getTrackStats).mockResolvedValue(mockStats);

    render(<TrackStatsDisplay trackId={123} showLabels={true} />);

    await waitFor(() => {
      expect(screen.getByText('Vues')).toBeInTheDocument();
      expect(screen.getByText('Likes')).toBeInTheDocument();
      expect(screen.getByText('Commentaires')).toBeInTheDocument();
      expect(screen.getByText('Téléchargements')).toBeInTheDocument();
      expect(screen.getByText("Temps d'écoute")).toBeInTheDocument();
    });
  });

  it('should format large numbers correctly', async () => {
    const largeStats = {
      views: 1500000,
      likes: 2500,
      comments: 500,
      total_play_time: 7200,
      downloads: 100,
    };

    vi.mocked(getTrackStats).mockResolvedValue(largeStats);

    render(<TrackStatsDisplay trackId={123} />);

    await waitFor(() => {
      expect(screen.getByText('1.5M')).toBeInTheDocument(); // views
      expect(screen.getByText('2.5K')).toBeInTheDocument(); // likes
    });
  });

  it('should format duration correctly', async () => {
    const statsWithDuration = {
      views: 100,
      likes: 10,
      comments: 5,
      total_play_time: 3665, // 1h 1m 5s
      downloads: 2,
    };

    vi.mocked(getTrackStats).mockResolvedValue(statsWithDuration);

    render(<TrackStatsDisplay trackId={123} />);

    await waitFor(() => {
      expect(screen.getByText('1h 1m')).toBeInTheDocument();
    });
  });

  it('should format duration with only minutes', async () => {
    const statsWithMinutes = {
      views: 100,
      likes: 10,
      comments: 5,
      total_play_time: 125, // 2m 5s
      downloads: 2,
    };

    vi.mocked(getTrackStats).mockResolvedValue(statsWithMinutes);

    render(<TrackStatsDisplay trackId={123} />);

    await waitFor(() => {
      expect(screen.getByText('2m 5s')).toBeInTheDocument();
    });
  });

  it('should format duration with only seconds', async () => {
    const statsWithSeconds = {
      views: 100,
      likes: 10,
      comments: 5,
      total_play_time: 45, // 45s
      downloads: 2,
    };

    vi.mocked(getTrackStats).mockResolvedValue(statsWithSeconds);

    render(<TrackStatsDisplay trackId={123} />);

    await waitFor(() => {
      expect(screen.getByText('45s')).toBeInTheDocument();
    });
  });

  it('should display error message on failure', async () => {
    const error = new TrackStatsError('Track introuvable', 'NOT_FOUND', false);
    vi.mocked(getTrackStats).mockRejectedValue(error);

    render(<TrackStatsDisplay trackId={999} />);

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

  it('should display generic error message on unknown error', async () => {
    vi.mocked(getTrackStats).mockRejectedValue(new Error('Unknown error'));

    render(<TrackStatsDisplay trackId={123} />);

    await waitFor(() => {
      expect(
        screen.getByText('Impossible de charger les statistiques'),
      ).toBeInTheDocument();
    });
  });

  it('should reload stats when trackId changes', async () => {
    vi.mocked(getTrackStats).mockResolvedValue(mockStats);

    const { rerender } = render(<TrackStatsDisplay trackId={123} />);

    await waitFor(() => {
      expect(getTrackStats).toHaveBeenCalledWith(123);
    });

    vi.mocked(getTrackStats).mockResolvedValue({
      ...mockStats,
      views: 2000,
    });

    rerender(<TrackStatsDisplay trackId={456} />);

    await waitFor(() => {
      expect(getTrackStats).toHaveBeenCalledWith(456);
    });
  });

  it('should apply custom className', async () => {
    vi.mocked(getTrackStats).mockResolvedValue(mockStats);

    render(<TrackStatsDisplay trackId={123} className="custom-class" />);

    await waitFor(() => {
      const container = screen.getByText('1.0K').closest('div');
      expect(container?.className).toContain('custom-class');
    });
  });

  it('should handle zero values', async () => {
    const zeroStats = {
      views: 0,
      likes: 0,
      comments: 0,
      total_play_time: 0,
      downloads: 0,
    };

    vi.mocked(getTrackStats).mockResolvedValue(zeroStats);

    render(<TrackStatsDisplay trackId={123} />);

    await waitFor(() => {
      expect(screen.getByText('0')).toBeInTheDocument();
      expect(screen.getByText('0s')).toBeInTheDocument();
    });
  });
});
