// src/features/flyer/ExtractedDataTable.test.tsx
import React from 'react';
import { render, screen, fireEvent, within } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ExtractedDataTable, ExtractedDataTableProps } from './ExtractedDataTable';
import type { FlyerItem, MasterGroceryItem, ShoppingList, User } from '../../types';
import { useAuth } from '../../hooks/useAuth';
import { useUserData } from '../../hooks/useUserData';
import { useMasterItems } from '../../hooks/useMasterItems';
import { useWatchedItems } from '../../hooks/useWatchedItems';
import { useShoppingLists } from '../../hooks/useShoppingLists';

// Mock all the data hooks
vi.mock('../../hooks/useAuth');
vi.mock('../../hooks/useUserData');
vi.mock('../../hooks/useMasterItems');
vi.mock('../../hooks/useWatchedItems');
vi.mock('../../hooks/useShoppingLists');

const mockUser: User = { user_id: 'user-123', email: 'test@example.com' };

const mockMasterItems: MasterGroceryItem[] = [
  { master_grocery_item_id: 1, name: 'Apples', category_id: 1, category_name: 'Produce', created_at: '' },
  { master_grocery_item_id: 2, name: 'Milk', category_id: 2, category_name: 'Dairy', created_at: '' },
  { master_grocery_item_id: 3, name: 'Chicken Breast', category_id: 3, category_name: 'Meat', created_at: '' },
];

const mockFlyerItems: FlyerItem[] = [
  { flyer_item_id: 101, item: 'Gala Apples', price_display: '$1.99/lb', price_in_cents: 199, quantity: 'per lb', master_item_id: 1, category_name: 'Produce', flyer_id: 1, created_at: '', view_count: 0, click_count: 0, updated_at: '' },
  { flyer_item_id: 102, item: '2% Milk', price_display: '$4.50', price_in_cents: 450, quantity: '4L', master_item_id: 2, category_name: 'Dairy', flyer_id: 1, created_at: '', view_count: 0, click_count: 0, updated_at: '' },
  { flyer_item_id: 103, item: 'Boneless Chicken', price_display: '$8.00/kg', price_in_cents: 800, quantity: 'per kg', master_item_id: 3, category_name: 'Meat', flyer_id: 1, created_at: '', view_count: 0, click_count: 0, updated_at: '' },
  { flyer_item_id: 104, item: 'Mystery Soda', price_display: '$1.00', price_in_cents: 100, quantity: '1 can', master_item_id: undefined, category_name: 'Beverages', flyer_id: 1, created_at: '', view_count: 0, click_count: 0, updated_at: '' }, // Unmatched item
];

const mockShoppingLists: ShoppingList[] = [
  {
    shopping_list_id: 1, name: 'My List', user_id: 'user-123', created_at: '',
    items: [{ shopping_list_item_id: 1, shopping_list_id: 1, master_item_id: 2, quantity: 1, is_purchased: false, added_at: '' }], // Contains Milk
  },
];

const mockAddWatchedItem = vi.fn();
const mockAddItemToList = vi.fn();

const defaultProps: ExtractedDataTableProps = {
  items: mockFlyerItems,
  unitSystem: 'imperial' as const,
  // The following props are now sourced from the mocked hooks below,
  // so they are not needed here and have been removed from the component's signature.
  // totalActiveItems: mockFlyerItems.length,
  // watchedItems: [],
  // masterItems: mockMasterItems,
  // user: mockUser,
  // onAddItem: mockAddWatchedItem,
  // shoppingLists: mockShoppingLists,
  // activeListId: 1,
  // onAddItemToList: mockAddItemToList,
};

describe('ExtractedDataTable', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    // Provide default mocks for all hooks
    vi.mocked(useAuth).mockReturnValue({
      user: mockUser,
      profile: null,
      authStatus: 'AUTHENTICATED',
      isLoading: false,
      login: vi.fn(),
      logout: vi.fn(),
      updateProfile: vi.fn(),
    });
    vi.mocked(useUserData).mockReturnValue({
      watchedItems: [],
      shoppingLists: mockShoppingLists,
      setWatchedItems: vi.fn(),
      setShoppingLists: vi.fn(),
      isLoading: false,
      error: null,
    });
    vi.mocked(useMasterItems).mockReturnValue({
      masterItems: mockMasterItems,
      isLoading: false,
      error: null,
    });
    vi.mocked(useWatchedItems).mockReturnValue({
      addWatchedItem: mockAddWatchedItem,
      watchedItems: [],
      removeWatchedItem: vi.fn(),
      error: null,
    });
    vi.mocked(useShoppingLists).mockReturnValue({
      activeListId: 1,
      addItemToList: mockAddItemToList,
      shoppingLists: mockShoppingLists, // Add this to satisfy the component's dependency
      setActiveListId: vi.fn(),
      createList: vi.fn(),
      deleteList: vi.fn(),
      updateItemInList: vi.fn(),
      removeItemFromList: vi.fn(),
      isCreatingList: false, isDeletingList: false, isAddingItem: false, isUpdatingItem: false, isRemovingItem: false,
      error: null,
    });
  });

  it('should render an empty state message if no items are provided', () => {
    render(<ExtractedDataTable items={[]} unitSystem="imperial" />);
    expect(screen.getByText('No items extracted yet.')).toBeInTheDocument();
  });

  it('should render the table with items', () => {
    render(<ExtractedDataTable {...defaultProps} />);
    expect(screen.getByRole('heading', { name: /item list/i })).toBeInTheDocument();
    expect(screen.getByText('Gala Apples')).toBeInTheDocument();
    expect(screen.getByText('2% Milk')).toBeInTheDocument();
  });

  it('should not show watch/add to list buttons for anonymous users', () => {
    vi.mocked(useAuth).mockReturnValue({
      user: null,
      profile: null,
      authStatus: 'SIGNED_OUT',
      isLoading: false,
      login: vi.fn(),
      logout: vi.fn(),
      updateProfile: vi.fn(),
    });
    render(<ExtractedDataTable {...defaultProps} />);
    expect(screen.queryByRole('button', { name: /watch/i })).not.toBeInTheDocument();
    expect(screen.queryByRole('button', { name: /add to list/i })).not.toBeInTheDocument();
  });

  describe('Item States and Interactions', () => {
    it('should highlight watched items and hide the watch button', () => {
      vi.mocked(useUserData).mockReturnValue({
        watchedItems: [mockMasterItems[0]], // 'Apples' is watched
        shoppingLists: [],
        setWatchedItems: vi.fn(),
        setShoppingLists: vi.fn(),
        isLoading: false,
        error: null,
      });
      render(<ExtractedDataTable {...defaultProps} />);
      const appleItemRow = screen.getByText('Gala Apples').closest('tr');
      expect(appleItemRow).toHaveTextContent('Gala Apples');
      expect(appleItemRow!.querySelector('.font-bold')).toBeInTheDocument(); // Watched items are bold
      expect(appleItemRow!.querySelector('button[title*="Watch"]')).not.toBeInTheDocument();
    });

    it('should show the watch button for unwatched, matched items', () => {
      render(<ExtractedDataTable {...defaultProps} />);
      // Find the specific table row for the item first to make the test more specific.
      const chickenItemRow = screen.getByText('Boneless Chicken').closest('tr')!;
      // Now, find the watch button *within* that row.
      const watchButton = within(chickenItemRow).getByTitle("Add 'Chicken Breast' to your watchlist");
      expect(watchButton).toBeInTheDocument();

      fireEvent.click(watchButton);
      expect(mockAddWatchedItem).toHaveBeenCalledWith('Chicken Breast', 'Meat');
    });

    it('should not show watch or add to list buttons for unmatched items', () => {
      render(<ExtractedDataTable {...defaultProps} />);
      const sodaItemRow = screen.getByText('Mystery Soda').closest('tr');
      expect(sodaItemRow!.querySelector('button[title*="Watch"]')).not.toBeInTheDocument();
      expect(sodaItemRow!.querySelector('button[title*="list"]')).not.toBeInTheDocument();
    });

    it('should hide the add to list button for items already in the active list', () => {
      // 'Milk' (master_item_id: 2) is in the active shopping list
      vi.mocked(useShoppingLists).mockReturnValue({
        activeListId: 1,
        addItemToList: mockAddItemToList,
        shoppingLists: mockShoppingLists, // 'Milk' is in this list
        setActiveListId: vi.fn(),
        createList: vi.fn(),
        deleteList: vi.fn(),
        updateItemInList: vi.fn(),
        removeItemFromList: vi.fn(),
        isCreatingList: false, isDeletingList: false, isAddingItem: false, isUpdatingItem: false, isRemovingItem: false,
        error: null,
      });
      render(<ExtractedDataTable {...defaultProps} />);
      const milkItemRow = screen.getByText('2% Milk').closest('tr')!;
      // Use a more specific query to target the "shopping list" button and not the "watchlist" button.
      const addToListButton = within(milkItemRow).queryByTitle(/add milk to list/i);
      expect(addToListButton).not.toBeInTheDocument();
    });

    it('should show the add to list button for items not in the list', () => {
      vi.mocked(useShoppingLists).mockReturnValue({
        activeListId: 1,
        addItemToList: mockAddItemToList,
        shoppingLists: [{ ...mockShoppingLists[0], items: [] }], // Empty list
        setActiveListId: vi.fn(),
        createList: vi.fn(),
        deleteList: vi.fn(),
        updateItemInList: vi.fn(),
        removeItemFromList: vi.fn(),
        isCreatingList: false, isDeletingList: false, isAddingItem: false, isUpdatingItem: false, isRemovingItem: false,
        error: null,
      });
      render(<ExtractedDataTable {...defaultProps} />);
      const appleItemRow = screen.getByText('Gala Apples').closest('tr')!;
      // Correct the title query to match the actual rendered title.
      const addToListButton = within(appleItemRow).getByTitle("Add Apples to list");
      expect(addToListButton).toBeInTheDocument();

      fireEvent.click(addToListButton!);
      expect(mockAddItemToList).toHaveBeenCalledWith(1, { masterItemId: 1 }); // The component calls the hook's function
    });

    it('should disable the add to list button if no list is active', () => {
      vi.mocked(useShoppingLists).mockReturnValue({
        activeListId: null,
        shoppingLists: [],
        addItemToList: mockAddItemToList,
        setActiveListId: vi.fn(),
        createList: vi.fn(),
        deleteList: vi.fn(),
        updateItemInList: vi.fn(),
        removeItemFromList: vi.fn(),
        isCreatingList: false, isDeletingList: false, isAddingItem: false, isUpdatingItem: false, isRemovingItem: false,
        error: null,
      });
      render(<ExtractedDataTable {...defaultProps} />);
      // Multiple buttons will have this title, so we must use `getAllByTitle`.
      const addToListButtons = screen.getAllByTitle('Select a shopping list first');
      // Assert that at least one such button exists and that they are all disabled.
      expect(addToListButtons.length).toBeGreaterThan(0);
      addToListButtons.forEach(button => expect(button).toBeDisabled());
    });

    it('should display the canonical name when it differs from the item name', () => {
      render(<ExtractedDataTable {...defaultProps} />);
      // For 'Gala Apples', canonical is 'Apples'
      expect(screen.getByText('(Canonical: Apples)')).toBeInTheDocument();
      // For '2% Milk', canonical is 'Milk'
      expect(screen.getByText('(Canonical: Milk)')).toBeInTheDocument();
    });
  });

  describe('Sorting and Filtering', () => {
    it('should sort watched items to the top', () => {
      // Watch 'Chicken Breast' (normally 3rd) and 'Apples' (normally 1st)
      vi.mocked(useUserData).mockReturnValue({
        watchedItems: [mockMasterItems[2], mockMasterItems[0]],
        shoppingLists: [],
        setWatchedItems: vi.fn(),
        setShoppingLists: vi.fn(),
        isLoading: false,
        error: null,
      });
      render(<ExtractedDataTable {...defaultProps} />);
      
      // Get all rows from the table body
      const rows = screen.getAllByRole('row');
      // Extract the primary item name from each row to check the sort order
      const itemNamesInOrder = rows.map(row => row.querySelector('div.font-semibold, div.font-bold')?.textContent);

      // Assert the order is correct: watched items first, then others.
      // Note: The component doesn't specify a sub-sort, so the order among watched items is based on their original order.
      // 'Gala Apples' comes before 'Boneless Chicken' in the original `mockFlyerItems` array.
      expect(itemNamesInOrder).toEqual(['Gala Apples', 'Boneless Chicken', '2% Milk', 'Mystery Soda']);
    });

    it('should filter items by category', () => {
      render(<ExtractedDataTable {...defaultProps} />);
      const categoryFilter = screen.getByLabelText('Filter by category');
      
      // Initial state
      expect(screen.getByText('Gala Apples')).toBeInTheDocument();
      expect(screen.getByText('2% Milk')).toBeInTheDocument();

      // Filter by Dairy
      fireEvent.change(categoryFilter, { target: { value: 'Dairy' } });
      expect(screen.queryByText('Gala Apples')).not.toBeInTheDocument();
      expect(screen.getByText('2% Milk')).toBeInTheDocument();
      
      // Check empty state for filter
      fireEvent.change(categoryFilter, { target: { value: 'Produce' } });
      fireEvent.change(categoryFilter, { target: { value: 'Snacks' } }); // A category with no items
      expect(screen.getByText('No items found for the selected category.')).toBeInTheDocument();
    });
  });
});