// src/pages/admin/AdminStatsPage.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { MemoryRouter } from 'react-router-dom';
import { AdminStatsPage } from './AdminStatsPage';
import { useApplicationStatsQuery } from '../../hooks/queries/useApplicationStatsQuery';
import type { AppStats } from '../../services/apiClient';
import { createMockAppStats } from '../../tests/utils/mockFactories';
import { StatCard } from '../../components/StatCard';
import { QueryWrapper } from '../../tests/utils/renderWithProviders';

// Mock the TanStack Query hook
vi.mock('../../hooks/queries/useApplicationStatsQuery');

const mockedUseApplicationStatsQuery = vi.mocked(useApplicationStatsQuery);

// Mock the child StatCard component to use the shared mock and allow spying
vi.mock('../../components/StatCard', async () => {
  const { MockStatCard } = await import('../../tests/utils/componentMocks');
  return { StatCard: vi.fn(MockStatCard) };
});

// Get a reference to the mocked component
const mockedStatCard = StatCard as Mock;

// Helper function to render the component within router and query contexts
const renderWithRouter = () => {
  return render(
    <QueryWrapper>
      <MemoryRouter>
        <AdminStatsPage />
      </MemoryRouter>
    </QueryWrapper>,
  );
};

describe('AdminStatsPage', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedStatCard.mockClear();
    // Default mock implementation
    mockedUseApplicationStatsQuery.mockReturnValue({
      data: undefined,
      isLoading: false,
      error: null,
    } as any);
  });

  it('should render a loading spinner while fetching stats', async () => {
    mockedUseApplicationStatsQuery.mockReturnValue({
      data: undefined,
      isLoading: true,
      error: null,
    } as any);

    renderWithRouter();

    expect(screen.getByRole('status', { name: /loading/i })).toBeInTheDocument();
  });

  it('should display stats cards when data is fetched successfully', async () => {
    const mockStats: AppStats = createMockAppStats({
      userCount: 123,
      flyerCount: 456,
      flyerItemCount: 7890,
      storeCount: 42,
      pendingCorrectionCount: 5,
      recipeCount: 150,
    });
    mockedUseApplicationStatsQuery.mockReturnValue({
      data: mockStats,
      isLoading: false,
      error: null,
    } as any);

    renderWithRouter();

    expect(screen.getByText('Total Users')).toBeInTheDocument();
    expect(screen.getByText('123')).toBeInTheDocument();

    expect(screen.getByText('Flyers Processed')).toBeInTheDocument();
    expect(screen.getByText('456')).toBeInTheDocument();

    expect(screen.getByText('Total Flyer Items')).toBeInTheDocument();
    expect(screen.getByText('7,890')).toBeInTheDocument();

    expect(screen.getByText('Stores Tracked')).toBeInTheDocument();
    expect(screen.getByText('42')).toBeInTheDocument();

    expect(screen.getByText('Pending Corrections')).toBeInTheDocument();
    expect(screen.getByText('5')).toBeInTheDocument();

    expect(screen.getByText('Total Recipes')).toBeInTheDocument();
    expect(screen.getByText('150')).toBeInTheDocument();
  });

  it('should pass the correct props to each StatCard component', async () => {
    const mockStats: AppStats = createMockAppStats({
      userCount: 123,
      flyerCount: 456,
      flyerItemCount: 7890,
      storeCount: 42,
      pendingCorrectionCount: 5,
      recipeCount: 150,
    });
    mockedUseApplicationStatsQuery.mockReturnValue({
      data: mockStats,
      isLoading: false,
      error: null,
    } as any);

    renderWithRouter();

    // Verify it was called 6 times, once for each stat
    expect(mockedStatCard).toHaveBeenCalledTimes(6);

    // Check props for each card individually for robustness
    expect(mockedStatCard).toHaveBeenCalledWith(
      expect.objectContaining({
        title: 'Total Users',
        value: '123',
      }),
      undefined,
    );
    expect(mockedStatCard).toHaveBeenCalledWith(
      expect.objectContaining({
        title: 'Flyers Processed',
        value: '456',
      }),
      undefined,
    );
    expect(mockedStatCard).toHaveBeenCalledWith(
      expect.objectContaining({
        title: 'Total Flyer Items',
        value: '7,890',
      }),
      undefined,
    );
    expect(mockedStatCard).toHaveBeenCalledWith(
      expect.objectContaining({
        title: 'Stores Tracked',
        value: '42',
      }),
      undefined,
    );
    expect(mockedStatCard).toHaveBeenCalledWith(
      expect.objectContaining({
        title: 'Pending Corrections',
        value: '5',
      }),
      undefined,
    );
    expect(mockedStatCard).toHaveBeenCalledWith(
      expect.objectContaining({
        title: 'Total Recipes',
        value: '150',
      }),
      undefined,
    );
  });

  it('should format large numbers with commas for readability', async () => {
    const mockStats: AppStats = createMockAppStats({
      userCount: 1234567,
      flyerCount: 9876,
      flyerItemCount: 123456789,
      recipeCount: 50000,
    });
    mockedUseApplicationStatsQuery.mockReturnValue({
      data: mockStats,
      isLoading: false,
      error: null,
    } as any);

    renderWithRouter();

    expect(screen.getByText('1,234,567')).toBeInTheDocument();
    expect(screen.getByText('9,876')).toBeInTheDocument();
    expect(screen.getByText('123,456,789')).toBeInTheDocument();
    expect(screen.getByText('50,000')).toBeInTheDocument();
  });

  it('should correctly display zero values for all stats', async () => {
    const mockZeroStats: AppStats = createMockAppStats({
      userCount: 0,
      flyerCount: 0,
      flyerItemCount: 0,
      storeCount: 0,
      pendingCorrectionCount: 0,
      recipeCount: 0,
    });
    mockedUseApplicationStatsQuery.mockReturnValue({
      data: mockZeroStats,
      isLoading: false,
      error: null,
    } as any);

    renderWithRouter();

    // `getAllByText` will find all instances of '0'. There should be 6.
    const zeroValueElements = screen.getAllByText('0');
    expect(zeroValueElements).toHaveLength(6);

    // Also check that the titles are present to be sure we have the cards.
    expect(screen.getByText('Total Users')).toBeInTheDocument();
    expect(screen.getByText('Pending Corrections')).toBeInTheDocument();
  });

  it('should display an error message if fetching stats fails', async () => {
    const errorMessage = 'Failed to connect to the database.';
    mockedUseApplicationStatsQuery.mockReturnValue({
      data: undefined,
      isLoading: false,
      error: new Error(errorMessage),
    } as any);

    renderWithRouter();

    expect(screen.getByText(errorMessage)).toBeInTheDocument();
  });

  it('should render a link back to the admin dashboard', async () => {
    mockedUseApplicationStatsQuery.mockReturnValue({
      data: createMockAppStats(),
      isLoading: false,
      error: null,
    } as any);

    renderWithRouter();

    const link = screen.getByRole('link', { name: /back to admin dashboard/i });
    expect(link).toBeInTheDocument();
    expect(link).toHaveAttribute('href', '/admin');
  });
});
