// src/components/Leaderboard.test.tsx
import React from 'react';
import { screen, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import Leaderboard from './Leaderboard';
import * as apiClient from '../services/apiClient';
import { LeaderboardUser } from '../types';
import { createMockLeaderboardUser } from '../tests/utils/mockFactories';
import { renderWithProviders } from '../tests/utils/renderWithProviders';

// Must explicitly call vi.mock() for apiClient
vi.mock('../services/apiClient');

const mockedApiClient = vi.mocked(apiClient);

// Mock lucide-react icons to prevent rendering errors in the test environment
vi.mock('lucide-react', () => ({
  Award: () => <div data-testid="award-icon" />,
  Crown: () => <div data-testid="crown-icon" />,
  ShieldAlert: () => <div data-testid="shield-alert-icon" />,
}));

const mockLeaderboardData: LeaderboardUser[] = [
  createMockLeaderboardUser({ user_id: 'user-1', full_name: 'Alice', points: 1000, rank: '1' }),
  createMockLeaderboardUser({
    user_id: 'user-2',
    full_name: 'Bob',
    avatar_url: 'https://example.com/bob.jpg',
    points: 950,
    rank: '2',
  }),
  createMockLeaderboardUser({ user_id: 'user-3', full_name: 'Charlie', points: 900, rank: '3' }),
  createMockLeaderboardUser({ user_id: 'user-4', full_name: 'Diana', points: 850, rank: '4' }),
];

describe('Leaderboard', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should display a loading message initially', () => {
    // Mock a pending promise that never resolves to keep it in the loading state
    mockedApiClient.fetchLeaderboard.mockReturnValue(new Promise(() => {}));
    renderWithProviders(<Leaderboard />);
    expect(screen.getByText('Loading Leaderboard...')).toBeInTheDocument();
  });

  it('should display an error message if the API call fails', async () => {
    mockedApiClient.fetchLeaderboard.mockResolvedValue(new Response(null, { status: 500 }));
    renderWithProviders(<Leaderboard />);

    await waitFor(() => {
      expect(screen.getByRole('alert')).toBeInTheDocument();
      // The query hook throws an error with the status code when JSON parsing fails
      expect(screen.getByText('Error: Request failed with status 500')).toBeInTheDocument();
    });
  });

  it('should display a generic error for unknown error types', async () => {
    // Use an actual Error object since the component displays error.message
    mockedApiClient.fetchLeaderboard.mockRejectedValue(new Error('A string error'));
    renderWithProviders(<Leaderboard />);

    await waitFor(() => {
      expect(screen.getByRole('alert')).toBeInTheDocument();
      expect(screen.getByText('Error: A string error')).toBeInTheDocument();
    });
  });

  it('should display a message when the leaderboard is empty', async () => {
    mockedApiClient.fetchLeaderboard.mockResolvedValue(new Response(JSON.stringify([])));
    renderWithProviders(<Leaderboard />);

    await waitFor(() => {
      expect(
        screen.getByText('The leaderboard is currently empty. Be the first to earn points!'),
      ).toBeInTheDocument();
    });
  });

  it('should render the leaderboard with user data on successful fetch', async () => {
    mockedApiClient.fetchLeaderboard.mockResolvedValue(
      new Response(JSON.stringify(mockLeaderboardData)),
    );
    renderWithProviders(<Leaderboard />);

    await waitFor(() => {
      expect(screen.getByRole('heading', { name: 'Top Users' })).toBeInTheDocument();

      // Check for user details
      expect(screen.getByText('Alice')).toBeInTheDocument();
      expect(screen.getByText('1000 pts')).toBeInTheDocument();

      expect(screen.getByText('Bob')).toBeInTheDocument();
      expect(screen.getByText('950 pts')).toBeInTheDocument();

      // Check for correct avatar URLs
      const bobAvatar = screen.getByAltText('Bob') as HTMLImageElement;
      expect(bobAvatar.src).toBe('https://example.com/bob.jpg');

      const aliceAvatar = screen.getByAltText('Alice') as HTMLImageElement;
      expect(aliceAvatar.src).toContain('api.dicebear.com'); // Check for fallback avatar
    });
  });

  it('should render the correct rank icons', async () => {
    mockedApiClient.fetchLeaderboard.mockResolvedValue(
      new Response(JSON.stringify(mockLeaderboardData)),
    );
    renderWithProviders(<Leaderboard />);

    await waitFor(() => {
      // Rank 1, 2, and 3 should have a crown icon
      const crownIcons = screen.getAllByTestId('crown-icon');
      expect(crownIcons).toHaveLength(3);

      // Rank 4 should display the number
      expect(screen.getByText('4')).toBeInTheDocument();
    });
  });

  it('should handle users with missing names correctly', async () => {
    const dataWithMissingNames: LeaderboardUser[] = [
      createMockLeaderboardUser({ user_id: 'user-anon', full_name: null, points: 500, rank: '5' }),
    ];
    mockedApiClient.fetchLeaderboard.mockResolvedValue(
      new Response(JSON.stringify(dataWithMissingNames)),
    );
    renderWithProviders(<Leaderboard />);

    await waitFor(() => {
      // Check for fallback name
      expect(screen.getByText('Anonymous User')).toBeInTheDocument();

      // Check for fallback avatar alt text and URL
      const avatar = screen.getByAltText('User Avatar') as HTMLImageElement;
      expect(avatar).toBeInTheDocument();
      expect(avatar.src).toContain('api.dicebear.com');
      expect(avatar.src).toContain('seed=user-anon');
    });
  });
});
