// src/hooks/useFlyerItems.test.ts
import { renderHook } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useFlyerItems } from './useFlyerItems';
import { useApiOnMount } from './useApiOnMount';
import { createMockFlyer, createMockFlyerItem } from '../tests/utils/mockFactories';
import * as apiClient from '../services/apiClient';

// Mock the underlying useApiOnMount hook to isolate the useFlyerItems hook's logic.
vi.mock('./useApiOnMount');
vi.mock('../services/apiClient');

const mockedUseApiOnMount = vi.mocked(useApiOnMount);

describe('useFlyerItems Hook', () => {
  const mockFlyer = createMockFlyer({
    flyer_id: 123,
    file_name: 'test-flyer.jpg',
    image_url: '/test.jpg',
    icon_url: '/icon.jpg',
    checksum: 'abc',
    valid_from: '2024-01-01',
    valid_to: '2024-01-07',
    item_count: 1,
    store: {
      store_id: 1,
      name: 'Test Store',
    },
  });

  const mockFlyerItems = [
    createMockFlyerItem({
      flyer_item_id: 1,
      flyer_id: 123,
      item: 'Apples',
      price_display: '$1.99',
      price_in_cents: 199,
      quantity: '1lb',
    }),
  ];

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

  it('should return initial state and not call useApiOnMount when flyer is null', () => {
    // Arrange: Mock the return value of the inner hook.
    mockedUseApiOnMount.mockReturnValue({
      data: null,
      loading: false,
      error: null,
      isRefetching: false,
      reset: vi.fn(),
    });

    // Act: Render the hook with a null flyer.
    const { result } = renderHook(() => useFlyerItems(null));

    // Assert: Check that the hook returns the correct initial state.
    expect(result.current.flyerItems).toEqual([]);
    expect(result.current.isLoading).toBe(false);
    expect(result.current.error).toBeNull();

    // Assert: Check that useApiOnMount was called with `enabled: false`.
    expect(mockedUseApiOnMount).toHaveBeenCalledWith(
      expect.any(Function), // the wrapped fetcher function
      [null], // dependencies array
      { enabled: false }, // options object
      undefined, // flyer_id
    );
  });

  it('should call useApiOnMount with enabled: true when a flyer is provided', () => {
    mockedUseApiOnMount.mockReturnValue({
      data: null,
      loading: true,
      error: null,
      isRefetching: false,
      reset: vi.fn(),
    });

    renderHook(() => useFlyerItems(mockFlyer));

    // Assert: Check that useApiOnMount was called with the correct parameters.
    expect(mockedUseApiOnMount).toHaveBeenCalledWith(
      expect.any(Function),
      [mockFlyer],
      { enabled: true },
      mockFlyer.flyer_id,
    );
  });

  it('should return isLoading: true when the inner hook is loading', () => {
    mockedUseApiOnMount.mockReturnValue({
      data: null,
      loading: true,
      error: null,
      isRefetching: false,
      reset: vi.fn(),
    });

    const { result } = renderHook(() => useFlyerItems(mockFlyer));

    expect(result.current.isLoading).toBe(true);
  });

  it('should return flyerItems when the inner hook provides data', () => {
    mockedUseApiOnMount.mockReturnValue({
      data: { items: mockFlyerItems },
      loading: false,
      error: null,
      isRefetching: false,
      reset: vi.fn(),
    });

    const { result } = renderHook(() => useFlyerItems(mockFlyer));

    expect(result.current.isLoading).toBe(false);
    expect(result.current.flyerItems).toEqual(mockFlyerItems);
    expect(result.current.error).toBeNull();
  });

  it('should return an error when the inner hook returns an error', () => {
    const mockError = new Error('Failed to fetch');
    mockedUseApiOnMount.mockReturnValue({
      data: null,
      loading: false,
      error: mockError,
      isRefetching: false,
      reset: vi.fn(),
    });

    const { result } = renderHook(() => useFlyerItems(mockFlyer));

    expect(result.current.isLoading).toBe(false);
    expect(result.current.flyerItems).toEqual([]);
    expect(result.current.error).toEqual(mockError);
  });

  describe('wrappedFetcher behavior', () => {
    it('should reject if called with undefined flyerId', async () => {
      // We need to trigger the hook to get access to the internal wrappedFetcher
      mockedUseApiOnMount.mockReturnValue({
        data: null,
        loading: false,
        error: null,
        isRefetching: false,
        reset: vi.fn(),
      });
      renderHook(() => useFlyerItems(mockFlyer));

      // The first argument passed to useApiOnMount is the wrappedFetcher function
      const wrappedFetcher = mockedUseApiOnMount.mock.calls[0][0];

      // Verify the fetcher rejects when no ID is passed (which shouldn't happen in normal flow due to 'enabled')
      await expect(wrappedFetcher(undefined)).rejects.toThrow(
        'Cannot fetch items for an undefined flyer ID.',
      );
    });

    it('should call apiClient.fetchFlyerItems when called with a valid ID', async () => {
      mockedUseApiOnMount.mockReturnValue({
        data: null,
        loading: false,
        error: null,
        isRefetching: false,
        reset: vi.fn(),
      });
      renderHook(() => useFlyerItems(mockFlyer));

      const wrappedFetcher = mockedUseApiOnMount.mock.calls[0][0];
      const mockResponse = new Response();
      vi.mocked(apiClient.fetchFlyerItems).mockResolvedValue(mockResponse);

      const response = await wrappedFetcher(123);

      expect(apiClient.fetchFlyerItems).toHaveBeenCalledWith(123);
      expect(response).toBe(mockResponse);
    });
  });
});
