// src/hooks/useWatchedItems.test.tsx
import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useWatchedItems } from './useWatchedItems';
import { useAuth } from '../hooks/useAuth';
import { useUserData } from '../hooks/useUserData';
import { useAddWatchedItemMutation, useRemoveWatchedItemMutation } from './mutations';
import type { User } from '../types';
import {
  createMockMasterGroceryItem,
  createMockUser,
  createMockUserProfile,
} from '../tests/utils/mockFactories';

// Mock the hooks that useWatchedItems depends on
vi.mock('../hooks/useAuth');
vi.mock('../hooks/useUserData');
vi.mock('./mutations', () => ({
  useAddWatchedItemMutation: vi.fn(),
  useRemoveWatchedItemMutation: vi.fn(),
}));

const mockedUseAuth = vi.mocked(useAuth);
const mockedUseUserData = vi.mocked(useUserData);
const mockedUseAddWatchedItemMutation = vi.mocked(useAddWatchedItemMutation);
const mockedUseRemoveWatchedItemMutation = vi.mocked(useRemoveWatchedItemMutation);

const mockUser: User = createMockUser({ user_id: 'user-123', email: 'test@example.com' });
const mockInitialItems = [
  createMockMasterGroceryItem({ master_grocery_item_id: 1, name: 'Milk' }),
  createMockMasterGroceryItem({ master_grocery_item_id: 2, name: 'Bread' }),
];

describe('useWatchedItems Hook', () => {
  const mockMutateAsync = vi.fn();
  const mockAddMutation = {
    mutateAsync: mockMutateAsync,
    mutate: vi.fn(),
    isPending: false,
    error: null,
    isError: false,
    isSuccess: false,
    isIdle: true,
  };
  const mockRemoveMutation = {
    mutateAsync: mockMutateAsync,
    mutate: vi.fn(),
    isPending: false,
    error: null,
    isError: false,
    isSuccess: false,
    isIdle: true,
  };

  beforeEach(() => {
    vi.resetAllMocks();

    // Mock TanStack Query mutation hooks
    mockedUseAddWatchedItemMutation.mockReturnValue(mockAddMutation as any);
    mockedUseRemoveWatchedItemMutation.mockReturnValue(mockRemoveMutation as any);

    // Provide default implementation for auth
    mockedUseAuth.mockReturnValue({
      userProfile: createMockUserProfile({ user: mockUser }),
      authStatus: 'AUTHENTICATED',
      isLoading: false,
      login: vi.fn(),
      logout: vi.fn(),
      updateProfile: vi.fn(),
    });

    // Provide default implementation for user data (no more setters!)
    mockedUseUserData.mockReturnValue({
      watchedItems: mockInitialItems,
      shoppingLists: [],
      isLoading: false,
      error: null,
    });
  });

  it('should initialize with the watched items from useData', () => {
    const { result } = renderHook(() => useWatchedItems());

    expect(result.current.watchedItems).toEqual(mockInitialItems);
    expect(result.current.error).toBeNull();
  });

  it('should use TanStack Query mutation hooks', () => {
    renderHook(() => useWatchedItems());

    // Verify that the mutation hooks were called
    expect(mockedUseAddWatchedItemMutation).toHaveBeenCalled();
    expect(mockedUseRemoveWatchedItemMutation).toHaveBeenCalled();
  });

  describe('addWatchedItem', () => {
    it('should call the mutation with correct parameters', async () => {
      mockMutateAsync.mockResolvedValue({});

      const { result } = renderHook(() => useWatchedItems());

      await act(async () => {
        await result.current.addWatchedItem('Cheese', 'Dairy');
      });

      // Verify mutation was called with correct parameters
      expect(mockMutateAsync).toHaveBeenCalledWith({
        itemName: 'Cheese',
        category: 'Dairy',
      });
    });

    it('should expose error from mutation', () => {
      const errorMutation = {
        ...mockAddMutation,
        error: new Error('API Error'),
      };
      mockedUseAddWatchedItemMutation.mockReturnValue(errorMutation as any);

      const { result } = renderHook(() => useWatchedItems());

      expect(result.current.error).toBe('API Error');
    });

    it('should handle mutation errors gracefully', async () => {
      mockMutateAsync.mockRejectedValue(new Error('Failed to add'));

      const { result } = renderHook(() => useWatchedItems());

      await act(async () => {
        await result.current.addWatchedItem('Failing Item', 'Error');
      });

      // Should not throw - error is caught and logged
      expect(mockMutateAsync).toHaveBeenCalled();
    });
  });

  describe('removeWatchedItem', () => {
    it('should call the mutation with correct parameters', async () => {
      mockMutateAsync.mockResolvedValue({});

      const { result } = renderHook(() => useWatchedItems());

      await act(async () => {
        await result.current.removeWatchedItem(1);
      });

      // Verify mutation was called with correct parameters
      expect(mockMutateAsync).toHaveBeenCalledWith({
        masterItemId: 1,
      });
    });

    it('should expose error from remove mutation', () => {
      const errorMutation = {
        ...mockRemoveMutation,
        error: new Error('Deletion Failed'),
      };
      mockedUseRemoveWatchedItemMutation.mockReturnValue(errorMutation as any);

      const { result } = renderHook(() => useWatchedItems());

      expect(result.current.error).toBe('Deletion Failed');
    });

    it('should handle mutation errors gracefully', async () => {
      mockMutateAsync.mockRejectedValue(new Error('Failed to remove'));

      const { result } = renderHook(() => useWatchedItems());

      await act(async () => {
        await result.current.removeWatchedItem(999);
      });

      // Should not throw - error is caught and logged
      expect(mockMutateAsync).toHaveBeenCalled();
    });
  });

  it('should not perform actions if the user is not authenticated', async () => {
    mockedUseAuth.mockReturnValue({
      userProfile: null,
      authStatus: 'SIGNED_OUT',
      isLoading: false,
      login: vi.fn(),
      logout: vi.fn(),
      updateProfile: vi.fn(),
    });

    const { result } = renderHook(() => useWatchedItems());

    await act(async () => {
      await result.current.addWatchedItem('Test', 'Category');
      await result.current.removeWatchedItem(1);
    });

    // Mutations should not be called when user is not authenticated
    expect(mockMutateAsync).not.toHaveBeenCalled();
  });
});
