// src/hooks/useFlyers.test.tsx
import React, { ReactNode } from 'react';
import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useFlyers } from './useFlyers';
import { FlyersProvider } from '../providers/FlyersProvider';
import { useFlyersQuery } from './queries/useFlyersQuery';
import type { Flyer } from '../types';
import { createMockFlyer } from '../tests/utils/mockFactories';

// 1. Mock the useFlyersQuery hook, which is the dependency of our FlyersProvider.
vi.mock('./queries/useFlyersQuery');

// 2. Create a typed mock of the hook for type safety and autocompletion.
const mockedUseFlyersQuery = vi.mocked(useFlyersQuery);

// 3. A simple wrapper component that renders our provider.
// This is necessary because the useFlyers hook needs to be a child of FlyersProvider.
const wrapper = ({ children }: { children: ReactNode }) => (
  <FlyersProvider>{children}</FlyersProvider>
);

describe('useFlyers Hook and FlyersProvider', () => {
  // Create mock functions that we can spy on to see if they are called.
  const mockRefetch = vi.fn();

  beforeEach(() => {
    // Clear mock history before each test to ensure test isolation.
    vi.clearAllMocks();
  });

  it('should throw an error if useFlyers is used outside of FlyersProvider', () => {
    // Suppress the expected console.error for this test to keep the output clean.
    const originalError = console.error;
    console.error = vi.fn();

    // Expecting renderHook to throw an error because there's no provider.
    expect(() => renderHook(() => useFlyers())).toThrow(
      'useFlyers must be used within a FlyersProvider',
    );

    // Restore the original console.error function.
    console.error = originalError;
  });

  it('should return the initial loading state correctly', () => {
    // Arrange: Configure the mocked hook to return a loading state.
    mockedUseFlyersQuery.mockReturnValue({
      data: undefined,
      isLoading: true,
      error: null,
      refetch: mockRefetch,
      isRefetching: false,
      // TanStack Query properties (partial mock)
      status: 'pending',
      fetchStatus: 'fetching',
      isPending: true,
      isSuccess: false,
      isError: false,
      isFetched: false,
      isFetchedAfterMount: false,
      isStale: false,
      isPlaceholderData: false,
      dataUpdatedAt: 0,
      errorUpdatedAt: 0,
      failureCount: 0,
      failureReason: null,
      errorUpdateCount: 0,
      isInitialLoading: true,
      isLoadingError: false,
      isRefetchError: false,
      promise: Promise.resolve([]),
    } as any);

    // Act: Render the hook within the provider wrapper.
    const { result } = renderHook(() => useFlyers(), { wrapper });

    // Assert: Check that the context values match the loading state.
    expect(result.current.isLoadingFlyers).toBe(true);
    expect(result.current.flyers).toEqual([]);
    expect(result.current.flyersError).toBeNull();
  });

  it('should return flyers data on successful fetch', () => {
    // Arrange: Mock a successful data fetch.
    const mockFlyers: Flyer[] = [
      createMockFlyer({
        flyer_id: 1,
        file_name: 'flyer1.jpg',
        image_url: 'https://example.com/flyer1.jpg',
        item_count: 5,
        created_at: '2024-01-01',
      }),
    ];
    mockedUseFlyersQuery.mockReturnValue({
      data: mockFlyers,
      isLoading: false,
      error: null,
      refetch: mockRefetch,
      isRefetching: false,
      status: 'success',
      fetchStatus: 'idle',
      isPending: false,
      isSuccess: true,
      isError: false,
      isFetched: true,
      isFetchedAfterMount: true,
      isStale: false,
      isPlaceholderData: false,
      dataUpdatedAt: Date.now(),
      errorUpdatedAt: 0,
      failureCount: 0,
      failureReason: null,
      errorUpdateCount: 0,
      isInitialLoading: false,
      isLoadingError: false,
      isRefetchError: false,
      promise: Promise.resolve(mockFlyers),
    } as any);

    // Act
    const { result } = renderHook(() => useFlyers(), { wrapper });

    // Assert
    expect(result.current.isLoadingFlyers).toBe(false);
    expect(result.current.flyers).toEqual(mockFlyers);
    // Note: hasNextFlyersPage is always false now since we're not using infinite query
    expect(result.current.hasNextFlyersPage).toBe(false);
  });

  it('should return an error state if the fetch fails', () => {
    // Arrange: Mock a failed data fetch.
    const mockError = new Error('Failed to fetch');
    mockedUseFlyersQuery.mockReturnValue({
      data: undefined,
      isLoading: false,
      error: mockError,
      refetch: mockRefetch,
      isRefetching: false,
      status: 'error',
      fetchStatus: 'idle',
      isPending: false,
      isSuccess: false,
      isError: true,
      isFetched: true,
      isFetchedAfterMount: true,
      isStale: false,
      isPlaceholderData: false,
      dataUpdatedAt: 0,
      errorUpdatedAt: Date.now(),
      failureCount: 1,
      failureReason: mockError,
      errorUpdateCount: 1,
      isInitialLoading: false,
      isLoadingError: true,
      isRefetchError: false,
      promise: Promise.resolve(undefined),
    } as any);

    // Act
    const { result } = renderHook(() => useFlyers(), { wrapper });

    // Assert
    expect(result.current.isLoadingFlyers).toBe(false);
    expect(result.current.flyers).toEqual([]);
    expect(result.current.flyersError).toBe(mockError);
  });

  it('should call refetchFlyers when the context function is invoked', () => {
    // Arrange
    mockedUseFlyersQuery.mockReturnValue({
      data: [],
      isLoading: false,
      error: null,
      refetch: mockRefetch,
      isRefetching: false,
      status: 'success',
      fetchStatus: 'idle',
      isPending: false,
      isSuccess: true,
      isError: false,
      isFetched: true,
      isFetchedAfterMount: true,
      isStale: false,
      isPlaceholderData: false,
      dataUpdatedAt: Date.now(),
      errorUpdatedAt: 0,
      failureCount: 0,
      failureReason: null,
      errorUpdateCount: 0,
      isInitialLoading: false,
      isLoadingError: false,
      isRefetchError: false,
      promise: Promise.resolve([]),
    } as any);
    const { result } = renderHook(() => useFlyers(), { wrapper });

    // Act
    act(() => {
      result.current.refetchFlyers();
    });

    // Assert
    expect(mockRefetch).toHaveBeenCalledTimes(1);
  });

  it('should have fetchNextFlyersPage as a no-op (infinite scroll not implemented)', () => {
    // Arrange
    mockedUseFlyersQuery.mockReturnValue({
      data: [],
      isLoading: false,
      error: null,
      refetch: mockRefetch,
      isRefetching: false,
      status: 'success',
      fetchStatus: 'idle',
      isPending: false,
      isSuccess: true,
      isError: false,
      isFetched: true,
      isFetchedAfterMount: true,
      isStale: false,
      isPlaceholderData: false,
      dataUpdatedAt: Date.now(),
      errorUpdatedAt: 0,
      failureCount: 0,
      failureReason: null,
      errorUpdateCount: 0,
      isInitialLoading: false,
      isLoadingError: false,
      isRefetchError: false,
      promise: Promise.resolve([]),
    } as any);
    const { result } = renderHook(() => useFlyers(), { wrapper });

    // Act & Assert: fetchNextFlyersPage should exist but be a no-op
    expect(result.current.fetchNextFlyersPage).toBeDefined();
    expect(typeof result.current.fetchNextFlyersPage).toBe('function');
    // Calling it should not throw
    expect(() => result.current.fetchNextFlyersPage()).not.toThrow();
  });
});
