// src/layouts/MainLayout.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import { MainLayout } from './MainLayout';

// Mock all custom hooks used by the layout
import { useAuth } from '../hooks/useAuth';
import { useFlyers } from '../hooks/useFlyers';
import { useMasterItems } from '../hooks/useMasterItems';
import { useShoppingLists } from '../hooks/useShoppingLists';
import { useWatchedItems } from '../hooks/useWatchedItems';
import { useActiveDeals } from '../hooks/useActiveDeals';

import type { ActivityLogItem, UserProfile } from '../types';

// Mock child components to simplify testing and focus on the layout's logic
vi.mock('../features/flyer/FlyerList', () => ({ FlyerList: () => <div data-testid="flyer-list" /> }));
vi.mock('../features/flyer/FlyerUploader', () => ({ FlyerUploader: () => <div data-testid="flyer-uploader" /> }));
vi.mock('../features/shopping/ShoppingList', () => ({ ShoppingListComponent: (props: { onSelectList: (id: number) => void }) => <div data-testid="shopping-list" onClick={() => props.onSelectList(2)} /> }));
vi.mock('../features/shopping/WatchedItemsList', () => ({ WatchedItemsList: () => <div data-testid="watched-items-list" /> }));
vi.mock('../features/charts/PriceChart', () => ({ PriceChart: () => <div data-testid="price-chart" /> }));
vi.mock('../features/charts/PriceHistoryChart', () => ({ PriceHistoryChart: () => <div data-testid="price-history-chart" /> }));
vi.mock('../components/Leaderboard', () => ({ default: () => <div data-testid="leaderboard" /> }));
vi.mock('../pages/admin/ActivityLog', () => ({ ActivityLog: (props: { onLogClick: (log: ActivityLogItem) => void }) => <div data-testid="activity-log" onClick={() => props.onLogClick({ action: 'list_shared', details: { shopping_list_id: 1, list_name: 'test', shared_with_name: 'test' } } as ActivityLogItem)} /> }));
vi.mock('../pages/admin/components/AnonymousUserBanner', () => ({ AnonymousUserBanner: () => <div data-testid="anonymous-banner" /> }));
vi.mock('../components/ErrorDisplay', () => ({ ErrorDisplay: ({ message }: { message: string }) => <div data-testid="error-display">{message}</div> }));

// Mock the hooks
vi.mock('../hooks/useAuth');
vi.mock('../hooks/useFlyers');
vi.mock('../hooks/useMasterItems');
vi.mock('../hooks/useShoppingLists');
vi.mock('../hooks/useWatchedItems');
vi.mock('../hooks/useActiveDeals');

const mockedUseAuth = vi.mocked(useAuth);
const mockedUseFlyers = vi.mocked(useFlyers);
const mockedUseMasterItems = vi.mocked(useMasterItems);
const mockedUseShoppingLists = vi.mocked(useShoppingLists);
const mockedUseWatchedItems = vi.mocked(useWatchedItems);
const mockedUseActiveDeals = vi.mocked(useActiveDeals);

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

// A simple component to render inside the Outlet for context testing
const OutletContent = () => <div>Outlet Content</div>;

const renderWithRouter = (ui: React.ReactElement) => {
  return render(
    <MemoryRouter initialEntries={['/']}>
      <Routes>
        <Route path="/" element={ui}>
          <Route index element={<OutletContent />} />
        </Route>
      </Routes>
    </MemoryRouter>
  );
};

describe('MainLayout Component', () => {
  const mockOnFlyerSelect = vi.fn();
  const mockOnOpenProfile = vi.fn();
  const mockSetActiveListId = vi.fn();

  // Define default mock return values for each hook
  const defaultUseAuthReturn = {
    user: null,
    authStatus: 'SIGNED_OUT' as const,
    profile: null,
    isLoading: false,
    login: vi.fn(),
    logout: vi.fn(),
    updateProfile: vi.fn(),
  } as const;
  const defaultUseFlyersReturn = {
    flyers: [{
      flyer_id: 1,
      file_name: 'flyer.jpg',
      created_at: new Date().toISOString(),
      image_url: 'http://example.com/flyer.jpg',
      item_count: 10,
    }],
    isLoadingFlyers: false,
    flyersError: null,
    refetchFlyers: vi.fn(),
    fetchNextFlyersPage: vi.fn(),
    hasNextFlyersPage: false,
    isRefetchingFlyers: false,
  };
  const defaultUseMasterItemsReturn = {
    masterItems: [],
    isLoading: false,
    error: null,
  };
  const defaultUseShoppingListsReturn = {
    shoppingLists: [],
    activeListId: null,
    setActiveListId: mockSetActiveListId,
    createList: vi.fn(),
    deleteList: vi.fn(),
    addItemToList: vi.fn(),
    updateItemInList: vi.fn(),
    removeItemFromList: vi.fn(),
    isCreatingList: false,
    isDeletingList: false,
    isAddingItem: false,
    isUpdatingItem: false,
    isRemovingItem: false,
    error: null,
  };
  const defaultUseWatchedItemsReturn = {
    watchedItems: [],
    addWatchedItem: vi.fn(),
    removeWatchedItem: vi.fn(),
    error: null,
  };
  const defaultUseActiveDealsReturn = {
    activeDeals: [],
    totalActiveItems: 0,
    isLoading: false,
    error: null
  };

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

    // Provide default mock implementations for all hooks
    mockedUseAuth.mockReturnValue(defaultUseAuthReturn);
    mockedUseFlyers.mockReturnValue(defaultUseFlyersReturn);
    mockedUseMasterItems.mockReturnValue(defaultUseMasterItemsReturn);
    mockedUseShoppingLists.mockReturnValue(defaultUseShoppingListsReturn);
    mockedUseWatchedItems.mockReturnValue(defaultUseWatchedItemsReturn);
    mockedUseActiveDeals.mockReturnValue(defaultUseActiveDealsReturn);
  });

  const defaultProps = {
    onFlyerSelect: mockOnFlyerSelect,
    selectedFlyerId: null,
    onOpenProfile: mockOnOpenProfile,
  };

  it('renders all main sections and the Outlet content', () => {
    renderWithRouter(<MainLayout {...defaultProps} />);

    expect(screen.getByTestId('flyer-list')).toBeInTheDocument();
    expect(screen.getByTestId('flyer-uploader')).toBeInTheDocument();
    expect(screen.getByTestId('shopping-list')).toBeInTheDocument();
    expect(screen.getByTestId('watched-items-list')).toBeInTheDocument();
    expect(screen.getByTestId('price-chart')).toBeInTheDocument();
    expect(screen.getByTestId('price-history-chart')).toBeInTheDocument();
    expect(screen.getByTestId('leaderboard')).toBeInTheDocument();
    expect(screen.getByTestId('activity-log')).toBeInTheDocument();
    expect(screen.getByText('Outlet Content')).toBeInTheDocument();
  });

  describe('for unauthenticated users', () => {
    it('shows the AnonymousUserBanner when signed out and flyers exist', () => {
      renderWithRouter(<MainLayout {...defaultProps} />);
      expect(screen.getByTestId('anonymous-banner')).toBeInTheDocument();
    });

    it('does not show the AnonymousUserBanner if there are no flyers', () => {
      mockedUseFlyers.mockReturnValueOnce({ ...defaultUseFlyersReturn, flyers: [] });
      renderWithRouter(<MainLayout {...defaultProps} />);
      expect(screen.queryByTestId('anonymous-banner')).not.toBeInTheDocument();
    });
  });

  describe('for authenticated users', () => {
    beforeEach(() => {
      mockedUseAuth.mockReturnValue({
        ...defaultUseAuthReturn,
        user: mockUser,
        authStatus: 'AUTHENTICATED',
        profile: { user: mockUser } as UserProfile,
      });
    });

    it('does not show the AnonymousUserBanner', () => {
      renderWithRouter(<MainLayout {...defaultProps} />);
      expect(screen.queryByTestId('anonymous-banner')).not.toBeInTheDocument();
    });
  });

  describe('Error Handling', () => {
    it('displays an error message if useData has an error', () => {
      mockedUseFlyers.mockReturnValueOnce({ ...defaultUseFlyersReturn, flyersError: new Error('Data Fetch Failed') });
      renderWithRouter(<MainLayout {...defaultProps} />);
      expect(screen.getByTestId('error-display')).toHaveTextContent('Data Fetch Failed');
    });

    it('displays an error message if useShoppingLists has an error', () => {
      mockedUseShoppingLists.mockReturnValueOnce({ ...defaultUseShoppingListsReturn, error: 'Shopping List Failed' });
      renderWithRouter(<MainLayout {...defaultProps} />);
      expect(screen.getByTestId('error-display')).toHaveTextContent('Shopping List Failed');
    });

    it('displays an error message if useWatchedItems has an error', () => {
      mockedUseWatchedItems.mockReturnValueOnce({ ...defaultUseWatchedItemsReturn, error: 'Watched Items Failed' });
      renderWithRouter(<MainLayout {...defaultProps} />);
      expect(screen.getByTestId('error-display')).toHaveTextContent('Watched Items Failed');
    });

    it('displays an error message if useActiveDeals has an error', () => {
      mockedUseActiveDeals.mockReturnValueOnce({ ...defaultUseActiveDealsReturn, error: 'Active Deals Failed' });
      renderWithRouter(<MainLayout {...defaultProps} />);
      expect(screen.getByTestId('error-display')).toHaveTextContent('Active Deals Failed');
    });
  });

  describe('Event Handlers', () => {
    it('calls setActiveListId when a list is shared via ActivityLog and the list exists', () => {
      mockedUseShoppingLists.mockReturnValueOnce({
        ...defaultUseShoppingListsReturn,
        shoppingLists: [{ shopping_list_id: 1, name: 'My List', user_id: 'user-123', created_at: '', items: [] }],
      });

      renderWithRouter(<MainLayout {...defaultProps} />);
      const activityLog = screen.getByTestId('activity-log');
      fireEvent.click(activityLog);

      expect(mockSetActiveListId).toHaveBeenCalledWith(1);
    });

    it('does not call setActiveListId if the shared list does not exist', () => {
      renderWithRouter(<MainLayout {...defaultProps} />);
      const activityLog = screen.getByTestId('activity-log');
      fireEvent.click(activityLog); // Mock click simulates sharing list with id 1

      expect(mockSetActiveListId).not.toHaveBeenCalled();
    });
  });
});