// src/pages/admin/ActivityLog.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ActivityLog } from './ActivityLog';
import { useActivityLogQuery } from '../../hooks/queries/useActivityLogQuery';
import type { ActivityLogItem, UserProfile } from '../../types';
import { createMockActivityLogItem, createMockUserProfile } from '../../tests/utils/mockFactories';
import { QueryWrapper } from '../../tests/utils/renderWithProviders';

// Mock the TanStack Query hook
vi.mock('../../hooks/queries/useActivityLogQuery');

const renderWithQuery = (ui: React.ReactElement) => render(ui, { wrapper: QueryWrapper });

const mockedUseActivityLogQuery = vi.mocked(useActivityLogQuery);

// Mock date-fns to return a consistent value for snapshots
vi.mock('date-fns', () => {
  return {
    formatDistanceToNow: vi.fn(() => 'about 5 hours ago'),
  };
});

const mockUserProfile: UserProfile = createMockUserProfile({
  user: { user_id: 'user-123', email: 'test@example.com' },
});

const mockLogs: ActivityLogItem[] = [
  createMockActivityLogItem({
    activity_log_id: 1,
    user_id: 'user-123',
    action: 'flyer_processed',
    display_text: 'Processed a new flyer for Walmart.',
    user_avatar_url: 'https://example.com/avatar.png',
    user_full_name: 'Test User',
    details: { flyer_id: 1, store_name: 'Walmart' },
  }),
  createMockActivityLogItem({
    activity_log_id: 2,
    user_id: 'user-456',
    action: 'recipe_created',
    display_text: 'Jane Doe added a new recipe: Pasta Carbonara',
    user_full_name: 'Jane Doe',
    details: { recipe_id: 1, recipe_name: 'Pasta Carbonara' },
  }),
  createMockActivityLogItem({
    activity_log_id: 3,
    user_id: 'user-789',
    action: 'list_shared',
    display_text: 'John Smith shared a list.',
    user_full_name: 'John Smith',
    details: { list_name: 'Weekly Groceries', shopping_list_id: 10, shared_with_name: 'Test User' },
  }),
  createMockActivityLogItem({
    activity_log_id: 4,
    user_id: 'user-101',
    action: 'user_registered',
    display_text: 'New user joined',
    details: { full_name: 'Newbie User' },
  }),
  createMockActivityLogItem({
    activity_log_id: 5,
    user_id: 'user-102',
    action: 'recipe_favorited',
    display_text: 'User favorited a recipe',
    user_full_name: 'Pizza Lover',
    user_avatar_url: 'https://example.com/pizza.png',
    details: { recipe_name: 'Best Pizza' },
  }),
  createMockActivityLogItem({
    activity_log_id: 6,
    user_id: 'user-103',
    action: 'unknown_action' as any,
    display_text: 'Something happened',
    details: {} as any,
  }),
];

describe('ActivityLog', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    // Default mock implementation
    mockedUseActivityLogQuery.mockReturnValue({
      data: [],
      isLoading: false,
      error: null,
    } as any);
  });

  it('should not render if userProfile is null', () => {
    const { container } = renderWithQuery(<ActivityLog userProfile={null} onLogClick={vi.fn()} />);
    expect(container).toBeEmptyDOMElement();
  });

  it('should show a loading state initially', async () => {
    mockedUseActivityLogQuery.mockReturnValue({
      data: undefined,
      isLoading: true,
      error: null,
    } as any);

    renderWithQuery(<ActivityLog userProfile={mockUserProfile} onLogClick={vi.fn()} />);

    expect(screen.getByText('Loading activity...')).toBeInTheDocument();
  });

  it('should display an error message if fetching logs fails', async () => {
    mockedUseActivityLogQuery.mockReturnValue({
      data: undefined,
      isLoading: false,
      error: new Error('API is down'),
    } as any);

    renderWithQuery(<ActivityLog userProfile={mockUserProfile} onLogClick={vi.fn()} />);
    expect(screen.getByText('API is down')).toBeInTheDocument();
  });

  it('should display a message when there are no logs', async () => {
    mockedUseActivityLogQuery.mockReturnValue({
      data: [],
      isLoading: false,
      error: null,
    } as any);

    renderWithQuery(<ActivityLog userProfile={mockUserProfile} onLogClick={vi.fn()} />);
    expect(screen.getByText('No recent activity to show.')).toBeInTheDocument();
  });

  it('should render a list of activities successfully covering all types', async () => {
    mockedUseActivityLogQuery.mockReturnValue({
      data: mockLogs,
      isLoading: false,
      error: null,
    } as any);

    renderWithQuery(<ActivityLog userProfile={mockUserProfile} />);

    // Check for specific text from different log types
    expect(screen.getByText('Walmart')).toBeInTheDocument();
    expect(screen.getByText('Pasta Carbonara')).toBeInTheDocument();
    expect(screen.getByText('Weekly Groceries')).toBeInTheDocument();
    expect(screen.getByText('Newbie User')).toBeInTheDocument();
    expect(screen.getByText('Best Pizza')).toBeInTheDocument();
    expect(screen.getByText('An unknown activity occurred.')).toBeInTheDocument();

    // Check for user names
    expect(screen.getByText('Jane Doe', { exact: false })).toBeInTheDocument();

    // Check for avatar
    const avatar = screen.getByAltText('Test User');
    expect(avatar).toBeInTheDocument();
    expect(avatar).toHaveAttribute('src', 'https://example.com/avatar.png');

    // Check for fallback avatar (Newbie User has no avatar)
    const newbieItem = screen.getByText('Newbie User').closest('li');
    const fallbackIcon = newbieItem?.querySelector('svg');
    expect(fallbackIcon).toBeInTheDocument();

    // Check for the mocked date
    expect(screen.getAllByText('about 5 hours ago')).toHaveLength(mockLogs.length);
  });

  it('should call onLogClick when a clickable log item is clicked', async () => {
    const onLogClickMock = vi.fn();
    mockedUseActivityLogQuery.mockReturnValue({
      data: mockLogs,
      isLoading: false,
      error: null,
    } as any);

    renderWithQuery(<ActivityLog userProfile={mockUserProfile} onLogClick={onLogClickMock} />);

    // Recipe Created
    const clickableRecipe = screen.getByText('Pasta Carbonara');
    fireEvent.click(clickableRecipe);
    expect(onLogClickMock).toHaveBeenCalledWith(mockLogs[1]);

    // List Shared
    const clickableList = screen.getByText('Weekly Groceries');
    fireEvent.click(clickableList);
    expect(onLogClickMock).toHaveBeenCalledWith(mockLogs[2]);

    // Recipe Favorited
    const clickableFav = screen.getByText('Best Pizza');
    fireEvent.click(clickableFav);
    expect(onLogClickMock).toHaveBeenCalledWith(mockLogs[4]);

    expect(onLogClickMock).toHaveBeenCalledTimes(3);
  });

  it('should not render clickable styling if onLogClick is undefined', async () => {
    mockedUseActivityLogQuery.mockReturnValue({
      data: mockLogs,
      isLoading: false,
      error: null,
    } as any);

    renderWithQuery(<ActivityLog userProfile={mockUserProfile} />);

    const recipeName = screen.getByText('Pasta Carbonara');
    expect(recipeName).not.toHaveClass('cursor-pointer');
    expect(recipeName).not.toHaveClass('text-blue-500');

    const listName = screen.getByText('Weekly Groceries');
    expect(listName).not.toHaveClass('cursor-pointer');
  });

  it('should handle missing details in logs gracefully (fallback values)', async () => {
    const logsWithMissingDetails: ActivityLogItem[] = [
      createMockActivityLogItem({
        activity_log_id: 101,
        user_id: 'u1',
        action: 'flyer_processed',
        display_text: '...',
        details: { flyer_id: 1, store_name: '' } as any,
      }),
      createMockActivityLogItem({
        activity_log_id: 102,
        user_id: 'u2',
        action: 'recipe_created',
        display_text: '...',
        details: { recipe_id: 1, recipe_name: '' } as any,
      }),
      createMockActivityLogItem({
        activity_log_id: 103,
        user_id: 'u3',
        action: 'user_registered',
        display_text: '...',
        details: { full_name: '' } as any,
      }),
      createMockActivityLogItem({
        activity_log_id: 104,
        user_id: 'u4',
        action: 'recipe_favorited',
        display_text: '...',
        details: { recipe_id: 2, recipe_name: '' } as any,
      }),
      createMockActivityLogItem({
        activity_log_id: 105,
        user_id: 'u5',
        action: 'list_shared',
        display_text: '...',
        details: { shopping_list_id: 1, list_name: '', shared_with_name: '' } as any,
      }),
      createMockActivityLogItem({
        activity_log_id: 106,
        user_id: 'u6',
        action: 'flyer_processed',
        display_text: '...',
        user_avatar_url: 'http://img.com/a.png',
        user_full_name: '',
        details: { flyer_id: 2, store_name: 'Mock Store' } as any,
      }),
    ];

    mockedUseActivityLogQuery.mockReturnValue({
      data: logsWithMissingDetails,
      isLoading: false,
      error: null,
    } as any);

    renderWithQuery(<ActivityLog userProfile={mockUserProfile} />);

    expect(screen.getAllByText('a store')[0]).toBeInTheDocument();
    expect(screen.getByText('Untitled Recipe')).toBeInTheDocument();
    expect(screen.getByText('A new user')).toBeInTheDocument();
    expect(screen.getByText('a recipe')).toBeInTheDocument();
    expect(screen.getByText('a shopping list')).toBeInTheDocument();
    expect(screen.getByText('another user')).toBeInTheDocument();

    // Check for avatar with fallback alt text
    const avatars = screen.getAllByRole('img');
    const avatarWithFallbackAlt = avatars.find((img) => img.getAttribute('alt') === 'User Avatar');
    expect(avatarWithFallbackAlt).toBeInTheDocument();
  });
});
