// src/pages/HomePage.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MemoryRouter, useOutletContext } from 'react-router-dom';
import { HomePage } from './HomePage';
import { createMockFlyer, createMockFlyerItem } from '../tests/utils/mockFactories';
import type { Flyer, FlyerItem } from '../types';

// Unmock the component to test the real implementation
vi.unmock('./HomePage');

// Mock child components to isolate the HomePage logic
vi.mock('../features/flyer/FlyerDisplay', async () => {
  const { MockFlyerDisplay } = await import('../tests/utils/componentMocks');
  return { FlyerDisplay: MockFlyerDisplay };
});
vi.mock('../features/flyer/AnalysisPanel', async () => {
  const { MockAnalysisPanel } = await import('../tests/utils/componentMocks');
  return { AnalysisPanel: MockAnalysisPanel };
});

// Mock the useOutletContext hook from react-router-dom
vi.mock('react-router-dom', async (importOriginal) => {
  const original = await importOriginal<typeof import('react-router-dom')>();
  return {
    ...original,
    useOutletContext: vi.fn(),
  };
});

// Mock ExtractedDataTable separately to use the imported props interface
import { ExtractedDataTable } from '../features/flyer/ExtractedDataTable';
vi.mock('../features/flyer/ExtractedDataTable', async () => {
  const { MockExtractedDataTable } = await import('../tests/utils/componentMocks');
  // Wrap the imported mock component in vi.fn() to allow spying on its calls.
  return { ExtractedDataTable: vi.fn(MockExtractedDataTable) };
});

const mockedUseOutletContext = vi.mocked(useOutletContext);

describe('HomePage Component', () => {
  const mockOnOpenCorrectionTool = vi.fn();

  // Define a default context that can be overridden in specific tests
  const mockContext = {
    totalActiveItems: 10,
    masterItems: [],
    addWatchedItem: vi.fn(),
    shoppingLists: [],
    activeListId: null,
    addItemToList: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
    // Provide the default mock implementation for the context hook
    mockedUseOutletContext.mockReturnValue(mockContext);
  });

  const renderInRouter = (ui: React.ReactElement) => {
    return render(<MemoryRouter>{ui}</MemoryRouter>);
  };

  it('should render the welcome message when no flyer is selected', () => {
    renderInRouter(
      <HomePage
        selectedFlyer={null}
        flyerItems={[]}
        onOpenCorrectionTool={mockOnOpenCorrectionTool}
      />,
    );

    expect(screen.getByRole('heading', { name: /welcome to flyer crawler/i })).toBeInTheDocument();
    expect(screen.getByText(/upload a new grocery flyer to begin/i)).toBeInTheDocument();
    expect(screen.queryByTestId('flyer-display')).not.toBeInTheDocument();
  });

  describe('when a flyer is selected', () => {
    const mockFlyer: Flyer = createMockFlyer({
      flyer_id: 1,
      image_url: 'http://example.com/flyer.jpg',
    });

    it('should render FlyerDisplay but not data tables if there are no flyer items', () => {
      renderInRouter(
        <HomePage
          selectedFlyer={mockFlyer}
          flyerItems={[]}
          onOpenCorrectionTool={mockOnOpenCorrectionTool}
        />,
      );

      const flyerDisplay = screen.getByTestId('flyer-display');
      expect(flyerDisplay).toBeInTheDocument();
      expect(flyerDisplay).toHaveAttribute('data-image-url', mockFlyer.image_url);

      expect(screen.queryByTestId('extracted-data-table')).not.toBeInTheDocument();
      expect(screen.queryByTestId('analysis-panel')).not.toBeInTheDocument();
    });

    it('should render all components when there are flyer items', () => {
      const mockItems: FlyerItem[] = [createMockFlyerItem({ flyer_item_id: 101 })];

      renderInRouter(
        <HomePage
          selectedFlyer={mockFlyer}
          flyerItems={mockItems}
          onOpenCorrectionTool={mockOnOpenCorrectionTool}
        />,
      );

      expect(screen.getByTestId('flyer-display')).toBeInTheDocument();
      expect(screen.getByTestId('extracted-data-table')).toBeInTheDocument();
      expect(screen.getByTestId('analysis-panel')).toBeInTheDocument();
    });

    it('should pass correct props to ExtractedDataTable', () => {
      const mockItems: FlyerItem[] = [
        createMockFlyerItem({ flyer_item_id: 101 }),
        createMockFlyerItem({ flyer_item_id: 102 }),
      ];

      renderInRouter(
        <HomePage
          selectedFlyer={mockFlyer}
          flyerItems={mockItems}
          onOpenCorrectionTool={mockOnOpenCorrectionTool}
        />,
      );

      // The mock component renders the number of items it received
      const dataTable = screen.getByTestId('extracted-data-table');
      expect(dataTable).toHaveTextContent('2 items');

      // We can also check the props passed to the mock component by inspecting the mock itself.
      // This is a more robust way to test prop passing.
      const ExtractedDataTableMock = vi.mocked(ExtractedDataTable);
      const props =
        ExtractedDataTableMock.mock.calls[ExtractedDataTableMock.mock.calls.length - 1][0];

      expect(props.items).toEqual(mockItems);
    });

    it('should call onOpenCorrectionTool when the correction tool button is clicked', () => {
      const mockItems: FlyerItem[] = [createMockFlyerItem({ flyer_item_id: 101 })];

      renderInRouter(
        <HomePage
          selectedFlyer={mockFlyer}
          flyerItems={mockItems}
          onOpenCorrectionTool={mockOnOpenCorrectionTool}
        />,
      );

      // The button is inside our updated MockFlyerDisplay
      const openButton = screen.getByTestId('mock-open-correction-tool');
      fireEvent.click(openButton);

      expect(mockOnOpenCorrectionTool).toHaveBeenCalledTimes(1);
    });
  });
});
