// src/hooks/useMasterItems.test.tsx
import React, { ReactNode } from 'react';
import { renderHook } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useMasterItems } from './useMasterItems';
import { MasterItemsProvider } from '../providers/MasterItemsProvider';
import { useMasterItemsQuery } from './queries/useMasterItemsQuery';
import type { MasterGroceryItem } from '../types';
import { createMockMasterGroceryItem } from '../tests/utils/mockFactories';

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

// 2. Create a typed mock for type safety and autocompletion.
const mockedUseMasterItemsQuery = vi.mocked(useMasterItemsQuery);

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

describe('useMasterItems Hook and MasterItemsProvider', () => {
  beforeEach(() => {
    // Clear mock history before each test to ensure test isolation.
    vi.clearAllMocks();
  });

  it('should throw an error if useMasterItems is used outside of MasterItemsProvider', () => {
    // 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(() => useMasterItems())).toThrow(
      'useMasterItems must be used within a MasterItemsProvider',
    );

    // 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.
    mockedUseMasterItemsQuery.mockReturnValue({
      data: undefined,
      isLoading: true,
      error: null,
    } as any);

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

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

  it('should return masterItems on successful fetch', () => {
    // Arrange: Mock a successful data fetch.
    const mockItems: MasterGroceryItem[] = [
      createMockMasterGroceryItem({
        master_grocery_item_id: 1,
        name: 'Milk',
        category_id: 1,
        category_name: 'Dairy',
      }),
      createMockMasterGroceryItem({
        master_grocery_item_id: 2,
        name: 'Bread',
        category_id: 2,
        category_name: 'Bakery',
      }),
    ];
    mockedUseMasterItemsQuery.mockReturnValue({
      data: mockItems,
      isLoading: false,
      error: null,
    } as any);

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

    // Assert
    expect(result.current.isLoading).toBe(false);
    expect(result.current.masterItems).toEqual(mockItems);
    expect(result.current.error).toBeNull();
  });

  it('should return an error state if the fetch fails', () => {
    // Arrange: Mock a failed data fetch.
    const mockError = new Error('Failed to fetch master items');
    mockedUseMasterItemsQuery.mockReturnValue({
      data: undefined,
      isLoading: false,
      error: mockError,
    } as any);

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

    // Assert
    expect(result.current.isLoading).toBe(false);
    expect(result.current.masterItems).toEqual([]);
    expect(result.current.error).toBe('Failed to fetch master items');
  });
});
