// src/hooks/queries/useFlyerItemsQuery.test.tsx
import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { ReactNode } from 'react';
import { useFlyerItemsQuery } from './useFlyerItemsQuery';
import * as apiClient from '../../services/apiClient';

vi.mock('../../services/apiClient');

const mockedApiClient = vi.mocked(apiClient);

describe('useFlyerItemsQuery', () => {
  let queryClient: QueryClient;

  const wrapper = ({ children }: { children: ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );

  beforeEach(() => {
    vi.resetAllMocks();
    queryClient = new QueryClient({
      defaultOptions: {
        queries: { retry: false },
      },
    });
  });

  it('should fetch flyer items when flyerId is provided', async () => {
    const mockFlyerItems = [
      { item_id: 1, name: 'Milk', price: 3.99, flyer_id: 42 },
      { item_id: 2, name: 'Bread', price: 2.49, flyer_id: 42 },
    ];
    // API returns wrapped response: { success: true, data: [...] }
    mockedApiClient.fetchFlyerItems.mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ success: true, data: mockFlyerItems }),
    } as Response);

    const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper });

    await waitFor(() => expect(result.current.isSuccess).toBe(true));

    expect(mockedApiClient.fetchFlyerItems).toHaveBeenCalledWith(42);
    expect(result.current.data).toEqual(mockFlyerItems);
  });

  it('should not fetch when flyerId is undefined', async () => {
    const { result } = renderHook(() => useFlyerItemsQuery(undefined), { wrapper });

    // Wait a bit to ensure the query doesn't run
    await new Promise((resolve) => setTimeout(resolve, 100));

    expect(mockedApiClient.fetchFlyerItems).not.toHaveBeenCalled();
    expect(result.current.isLoading).toBe(false);
    expect(result.current.isFetching).toBe(false);
  });

  it('should handle API error with error message', async () => {
    mockedApiClient.fetchFlyerItems.mockResolvedValue({
      ok: false,
      status: 404,
      json: () => Promise.resolve({ message: 'Flyer not found' }),
    } as Response);

    const { result } = renderHook(() => useFlyerItemsQuery(999), { wrapper });

    await waitFor(() => expect(result.current.isError).toBe(true));

    expect(result.current.error?.message).toBe('Flyer not found');
  });

  it('should handle API error without message', async () => {
    mockedApiClient.fetchFlyerItems.mockResolvedValue({
      ok: false,
      status: 500,
      json: () => Promise.reject(new Error('Parse error')),
    } as Response);

    const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper });

    await waitFor(() => expect(result.current.isError).toBe(true));

    expect(result.current.error?.message).toBe('Request failed with status 500');
  });

  it('should use fallback message when error.message is empty', async () => {
    mockedApiClient.fetchFlyerItems.mockResolvedValue({
      ok: false,
      status: 500,
      json: () => Promise.resolve({ message: '' }),
    } as Response);

    const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper });

    await waitFor(() => expect(result.current.isError).toBe(true));

    expect(result.current.error?.message).toBe('Failed to fetch flyer items');
  });

  // Note: The queryFn contains a guard `if (!flyerId) throw Error('Flyer ID is required')`
  // but this code path is unreachable in normal usage because the query has `enabled: !!flyerId`.
  // When enabled is false, calling refetch() does not execute the queryFn - React Query
  // respects the enabled condition. The guard exists as a defensive measure only.

  it('should return empty array when API returns no items', async () => {
    // API returns wrapped response: { success: true, data: [] }
    mockedApiClient.fetchFlyerItems.mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ success: true, data: [] }),
    } as Response);

    const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper });

    await waitFor(() => expect(result.current.isSuccess).toBe(true));

    expect(result.current.data).toEqual([]);
  });

  it('should return empty array when response lacks success/data structure (ADR-028)', async () => {
    // ADR-028: API must return { success: true, data: [...] }
    // Non-compliant responses return empty array to prevent .map() errors
    const legacyItems = [{ item_id: 1, name: 'Legacy Item' }];
    mockedApiClient.fetchFlyerItems.mockResolvedValue({
      ok: true,
      json: () => Promise.resolve(legacyItems),
    } as Response);

    const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper });

    await waitFor(() => expect(result.current.isSuccess).toBe(true));

    // Returns empty array when response doesn't match ADR-028 format
    expect(result.current.data).toEqual([]);
  });
});
