import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import RaceDetailPage from './page';
import type { RaceDetailsViewModel } from '@/lib/view-models/RaceDetailsViewModel';

// Mocks for Next.js navigation
const mockPush = vi.fn();
const mockBack = vi.fn();

vi.mock('next/navigation', () => ({
  useRouter: () => ({
    push: mockPush,
    back: mockBack,
  }),
  useParams: () => ({ id: 'race-123' }),
}));

// Mock effective driver id hook
vi.mock('@/hooks/useEffectiveDriverId', () => ({
  useEffectiveDriverId: () => 'driver-1',
}));

// Mock sponsor mode hook to avoid rendering heavy sponsor card
vi.mock('@/components/sponsors/SponsorInsightsCard', () => ({
  __esModule: true,
  default: () => <div data-testid="sponsor-insights-mock" />,
  MetricBuilders: {
    views: vi.fn(() => ({ label: 'Views', value: '100' })),
    engagement: vi.fn(() => ({ label: 'Engagement', value: '50%' })),
    reach: vi.fn(() => ({ label: 'Reach', value: '1000' })),
  },
  SlotTemplates: {
    race: vi.fn(() => []),
  },
  useSponsorMode: () => false,
}));

// Mock services hook to provide raceService and leagueMembershipService
const mockGetRaceDetails = vi.fn();
const mockReopenRace = vi.fn();
const mockFetchLeagueMemberships = vi.fn();
const mockGetMembership = vi.fn();

vi.mock('@/lib/services/ServiceProvider', () => ({
  useServices: () => ({
    raceService: {
      getRaceDetails: mockGetRaceDetails,
      reopenRace: mockReopenRace,
      // other methods are not used in this test
    },
    leagueMembershipService: {
      fetchLeagueMemberships: mockFetchLeagueMemberships,
      getMembership: mockGetMembership,
    },
  }),
}));

// Mock league membership utility to control admin vs non-admin behavior
const mockIsOwnerOrAdmin = vi.fn();

vi.mock('@/lib/utilities/LeagueMembershipUtility', () => ({
  LeagueMembershipUtility: {
    isOwnerOrAdmin: (...args: unknown[]) => mockIsOwnerOrAdmin(...args),
  },
}));

const renderWithQueryClient = (ui: React.ReactElement) => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

  return render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>);
};

const createViewModel = (status: string): RaceDetailsViewModel => {
  const canReopenRace = status === 'completed' || status === 'cancelled';

  return {
    race: {
      id: 'race-123',
      track: 'Test Track',
      car: 'Test Car',
      scheduledAt: '2023-12-31T20:00:00Z',
      status,
      sessionType: 'race',
    },
    league: {
      id: 'league-1',
      name: 'Test League',
      description: 'Test league description',
      settings: {
        maxDrivers: 32,
        qualifyingFormat: 'open',
      },
    },
    entryList: [],
    registration: {
      isUserRegistered: false,
      canRegister: false,
    },
    userResult: null,
    canReopenRace,
  };
};

describe('RaceDetailPage - Re-open Race behavior', () => {
  beforeEach(() => {
    mockGetRaceDetails.mockReset();
    mockReopenRace.mockReset();
    mockFetchLeagueMemberships.mockReset();
    mockGetMembership.mockReset();
    mockIsOwnerOrAdmin.mockReset();

    mockFetchLeagueMemberships.mockResolvedValue(undefined);
    mockGetMembership.mockReturnValue(null);
  });

  it('shows Re-open Race button for admin when race is completed and calls reopen + reload on confirm', async () => {
    mockIsOwnerOrAdmin.mockReturnValue(true);
    const viewModel = createViewModel('completed');

    // First call: initial load, second call: after re-open
    mockGetRaceDetails.mockResolvedValue(viewModel);

    const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true);

    renderWithQueryClient(<RaceDetailPage />);

    const reopenButtons = await screen.findAllByText('Re-open Race');
    const reopenButton = reopenButtons[0]!;
    expect(reopenButton).toBeInTheDocument();

    mockReopenRace.mockResolvedValue(undefined);

    fireEvent.click(reopenButton);

    await waitFor(() => {
      expect(mockReopenRace).toHaveBeenCalledWith('race-123');
    });

    // loadRaceData should be called again after reopening
    await waitFor(() => {
      expect(mockGetRaceDetails).toHaveBeenCalled();
    });

    confirmSpy.mockRestore();
  });

  it('does not render Re-open Race button for non-admin viewer', async () => {
    mockIsOwnerOrAdmin.mockReturnValue(false);
    const viewModel = createViewModel('completed');
    mockGetRaceDetails.mockResolvedValue(viewModel);

    renderWithQueryClient(<RaceDetailPage />);

    await waitFor(() => {
      expect(mockGetRaceDetails).toHaveBeenCalled();
    });

    expect(screen.queryByText('Re-open Race')).toBeNull();
  });

  it('does not render Re-open Race button when race is not completed or cancelled even for admin', async () => {
    mockIsOwnerOrAdmin.mockReturnValue(true);
    const viewModel = createViewModel('scheduled');
    mockGetRaceDetails.mockResolvedValue(viewModel);

    renderWithQueryClient(<RaceDetailPage />);

    await waitFor(() => {
      expect(mockGetRaceDetails).toHaveBeenCalled();
    });

    expect(screen.queryByText('Re-open Race')).toBeNull();
  });
});
