/**
 * Frontend Component Tests - Dashboard Components
 *
 * Tests for dashboard-related components:
 * - DashboardLayout
 * - StatsCard
 * - ChartComponent
 * - ActivityFeed
 * - NotificationPanel
 *
 * Author: Claude
 */

import React from 'react';
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
import '@testing-library/jest-dom';
import { BrowserRouter } from 'react-router-dom';

// Mock components for testing
const StatsCard = ({ title, value, change, changeType, icon }) => (
  <div data-testid="stats-card">
    <div data-testid="card-title">{title}</div>
    <div data-testid="card-value">{value}</div>
    {change && (
      <div data-testid={`card-change-${changeType}`}>
        {change > 0 ? '+' : ''}{change}%
      </div>
    )}
    {icon && <div data-testid="card-icon">{icon}</div>}
  </div>
);

const ChartComponent = ({ data, type, height = 300 }) => (
  <div data-testid="chart-component">
    <div data-testid="chart-type">{type}</div>
    <div data-testid="chart-height">{height}</div>
    <div data-testid="chart-data-points">{data?.length || 0}</div>
  </div>
);

const ActivityFeed = ({ activities, loading }) => (
  <div data-testid="activity-feed">
    {loading ? (
      <div data-testid="loading-spinner">Loading...</div>
    ) : (
      <div data-testid="activities-list">
        {activities?.map((activity, index) => (
          <div key={index} data-testid={`activity-${index}`}>
            <div data-testid="activity-type">{activity.type}</div>
            <div data-testid="activity-description">{activity.description}</div>
            <div data-testid="activity-time">{activity.timestamp}</div>
          </div>
        ))}
      </div>
    )}
  </div>
);

const NotificationPanel = ({ notifications, onMarkAsRead, onDismiss }) => (
  <div data-testid="notification-panel">
    <div data-testid="notifications-header">Notifications</div>
    <div data-testid="notifications-list">
      {notifications?.map((notification, index) => (
        <div key={index} data-testid={`notification-${index}`}>
          <div data-testid="notification-title">{notification.title}</div>
          <div data-testid="notification-message">{notification.message}</div>
          <div data-testid="notification-type">{notification.type}</div>
          <div data-testid="notification-read">{notification.read ? 'Read' : 'Unread'}</div>
          {!notification.read && (
            <button
              data-testid={`mark-read-${index}`}
              onClick={() => onMarkAsRead?.(notification.id)}
            >
              Mark as Read
            </button>
          )}
          <button
            data-testid={`dismiss-${index}`}
            onClick={() => onDismiss?.(notification.id)}
          >
            Dismiss
          </button>
        </div>
      ))}
    </div>
    {(!notifications || notifications.length === 0) && (
      <div data-testid="no-notifications">No notifications</div>
    )}
  </div>
);

// Test data
const mockActivities = [
  {
    id: 1,
    type: 'user_created',
    description: 'New user registered: John Doe',
    timestamp: '2024-01-15T10:30:00Z',
    user: { name: 'John Doe' }
  },
  {
    id: 2,
    type: 'payment_processed',
    description: 'Payment of RM299.00 processed',
    timestamp: '2024-01-15T09:15:00Z',
    amount: 299.00
  },
  {
    id: 3,
    type: 'login_attempt',
    description: 'Failed login attempt from unknown device',
    timestamp: '2024-01-15T08:00:00Z',
    successful: false
  }
];

const mockNotifications = [
  {
    id: 1,
    title: 'Welcome!',
    message: 'Welcome to the platform',
    type: 'info',
    read: false,
    timestamp: '2024-01-15T10:00:00Z'
  },
  {
    id: 2,
    title: 'Payment Received',
    message: 'Your subscription payment was successful',
    type: 'success',
    read: true,
    timestamp: '2024-01-14T15:30:00Z'
  },
  {
    id: 3,
    title: 'Security Alert',
    message: 'New login detected from unknown device',
    type: 'warning',
    read: false,
    timestamp: '2024-01-14T12:00:00Z'
  }
];

const mockChartData = [
  { month: 'Jan', value: 1000 },
  { month: 'Feb', value: 1200 },
  { month: 'Mar', value: 1100 },
  { month: 'Apr', value: 1400 },
  { month: 'May', value: 1300 },
  { month: 'Jun', value: 1500 }
];

describe('Dashboard Components', () => {
  describe('StatsCard', () => {
    test('renders stats card with basic props', () => {
      render(
        <StatsCard
          title="Total Users"
          value="1,234"
          change={12.5}
          changeType="positive"
          icon="👥"
        />
      );

      expect(screen.getByTestId('stats-card')).toBeInTheDocument();
      expect(screen.getByTestId('card-title')).toHaveTextContent('Total Users');
      expect(screen.getByTestId('card-value')).toHaveTextContent('1,234');
      expect(screen.getByTestId('card-change-positive')).toHaveTextContent('+12.5%');
      expect(screen.getByTestId('card-icon')).toHaveTextContent('👥');
    });

    test('renders stats card with negative change', () => {
      render(
        <StatsCard
          title="Bounce Rate"
          value="45.2%"
          change={-3.2}
          changeType="negative"
        />
      );

      expect(screen.getByTestId('card-change-negative')).toHaveTextContent('-3.2%');
    });

    test('renders stats card without change', () => {
      render(
        <StatsCard
          title="Total Revenue"
          value="RM 45,000"
        />
      );

      expect(screen.queryByTestId(/card-change-/)).not.toBeInTheDocument();
    });

    test('formats large numbers correctly', () => {
      const testCases = [
        { input: '1000000', expected: '1,000,000' },
        { input: '2500000.50', expected: '2,500,000.50' },
        { input: '1234567890', expected: '1,234,567,890' }
      ];

      testCases.forEach(({ input, expected }) => {
        render(<StatsCard title="Test" value={input} />);
        expect(screen.getByTestId('card-value')).toHaveTextContent(expected);
      });
    });
  });

  describe('ChartComponent', () => {
    test('renders chart with data', () => {
      render(
        <ChartComponent
          data={mockChartData}
          type="line"
          height={400}
        />
      );

      expect(screen.getByTestId('chart-component')).toBeInTheDocument();
      expect(screen.getByTestId('chart-type')).toHaveTextContent('line');
      expect(screen.getByTestId('chart-height')).toHaveTextContent('400');
      expect(screen.getByTestId('chart-data-points')).toHaveTextContent('6');
    });

    test('renders chart without data', () => {
      render(
        <ChartComponent
          data={[]}
          type="bar"
        />
      );

      expect(screen.getByTestId('chart-data-points')).toHaveTextContent('0');
    });

    test('renders chart with null data', () => {
      render(
        <ChartComponent
          data={null}
          type="pie"
        />
      );

      expect(screen.getByTestId('chart-data-points')).toHaveTextContent('0');
    });

    test('supports different chart types', () => {
      const chartTypes = ['line', 'bar', 'pie', 'area', 'scatter'];

      chartTypes.forEach(type => {
        const { unmount } = render(
          <ChartComponent
            data={mockChartData}
            type={type}
          />
        );

        expect(screen.getByTestId('chart-type')).toHaveTextContent(type);
        unmount();
      });
    });
  });

  describe('ActivityFeed', () => {
    test('renders activity feed with activities', () => {
      const mockOnActivityClick = jest.fn();

      render(
        <ActivityFeed
          activities={mockActivities}
          loading={false}
          onActivityClick={mockOnActivityClick}
        />
      );

      expect(screen.getByTestId('activity-feed')).toBeInTheDocument();
      expect(screen.getByTestId('activities-list')).toBeInTheDocument();
      expect(screen.getAllByTestId(/activity-/)).toHaveLength(3);

      // Check first activity
      expect(screen.getByTestId('activity-0')).toBeInTheDocument();
      expect(screen.getByTestId('activity-type')).toHaveTextContent('user_created');
      expect(screen.getByTestId('activity-description')).toHaveTextContent('New user registered: John Doe');
    });

    test('renders loading state', () => {
      render(
        <ActivityFeed
          activities={[]}
          loading={true}
        />
      );

      expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
      expect(screen.queryByTestId('activities-list')).not.toBeInTheDocument();
    });

    test('renders empty state', () => {
      render(
        <ActivityFeed
          activities={[]}
          loading={false}
        />
      );

      expect(screen.getByTestId('activities-list')).toBeInTheDocument();
      expect(screen.queryByTestId(/activity-/)).not.toBeInTheDocument();
    });

    test('formats timestamps correctly', () => {
      const formatTimestamp = (timestamp) => {
        const date = new Date(timestamp);
        const now = new Date();
        const diffMs = now - date;
        const diffMinutes = Math.floor(diffMs / 60000);
        const diffHours = Math.floor(diffMs / 3600000);
        const diffDays = Math.floor(diffMs / 86400000);

        if (diffMinutes < 1) return 'Just now';
        if (diffMinutes < 60) return `${diffMinutes}m ago`;
        if (diffHours < 24) return `${diffHours}h ago`;
        if (diffDays < 7) return `${diffDays}d ago`;
        return date.toLocaleDateString();
      };

      const recent = new Date().toISOString();
      const hourAgo = new Date(Date.now() - 3600000).toISOString();
      const dayAgo = new Date(Date.now() - 86400000).toISOString();

      expect(formatTimestamp(recent)).toMatch(/Just now|\d+m ago/);
      expect(formatTimestamp(hourAgo)).toMatch(/\d+h ago/);
      expect(formatTimestamp(dayAgo)).toMatch(/\d+d ago/);
    });

    test('handles activity click', () => {
      const mockOnActivityClick = jest.fn();

      render(
        <ActivityFeed
          activities={mockActivities}
          loading={false}
          onActivityClick={mockOnActivityClick}
        />
      );

      const firstActivity = screen.getByTestId('activity-0');
      fireEvent.click(firstActivity);

      expect(mockOnActivityClick).toHaveBeenCalledWith(mockActivities[0]);
    });
  });

  describe('NotificationPanel', () => {
    const mockOnMarkAsRead = jest.fn();
    const mockOnDismiss = jest.fn();

    beforeEach(() => {
      mockOnMarkAsRead.mockClear();
      mockOnDismiss.mockClear();
    });

    test('renders notification panel with notifications', () => {
      render(
        <NotificationPanel
          notifications={mockNotifications}
          onMarkAsRead={mockOnMarkAsRead}
          onDismiss={mockOnDismiss}
        />
      );

      expect(screen.getByTestId('notification-panel')).toBeInTheDocument();
      expect(screen.getByTestId('notifications-header')).toHaveTextContent('Notifications');
      expect(screen.getAllByTestId(/notification-/)).toHaveLength(3);
    });

    test('renders empty state', () => {
      render(
        <NotificationPanel
          notifications={[]}
          onMarkAsRead={mockOnMarkAsRead}
          onDismiss={mockOnDismiss}
        />
      );

      expect(screen.getByTestId('no-notifications')).toBeInTheDocument();
      expect(screen.getByTestId('no-notifications')).toHaveTextContent('No notifications');
    });

    test('handles mark as read action', async () => {
      render(
        <NotificationPanel
          notifications={mockNotifications}
          onMarkAsRead={mockOnMarkAsRead}
          onDismiss={mockOnDismiss}
        />
      );

      const firstNotification = screen.getByTestId('notification-0');
      const markReadButton = screen.getByTestId('mark-read-0');

      await act(async () => {
        fireEvent.click(markReadButton);
      });

      expect(mockOnMarkAsRead).toHaveBeenCalledWith(mockNotifications[0].id);
    });

    test('handles dismiss action', async () => {
      render(
        <NotificationPanel
          notifications={mockNotifications}
          onMarkAsRead={mockOnMarkAsRead}
          onDismiss={mockOnDismiss}
        />
      );

      const dismissButton = screen.getByTestId('dismiss-0');

      await act(async () => {
        fireEvent.click(dismissButton);
      });

      expect(mockOnDismiss).toHaveBeenCalledWith(mockNotifications[0].id);
    });

    test('shows read/unread status correctly', () => {
      render(
        <NotificationPanel
          notifications={mockNotifications}
          onMarkAsRead={mockOnMarkAsRead}
          onDismiss={mockOnDismiss}
        />
      );

      // First notification should be unread
      expect(screen.getByTestId('notification-0')).toHaveTextContent('Unread');
      expect(screen.getByTestId('mark-read-0')).toBeInTheDocument();

      // Second notification should be read
      expect(screen.getByTestId('notification-1')).toHaveTextContent('Read');
      expect(screen.queryByTestId('mark-read-1')).not.toBeInTheDocument();
    });

    test('shows different notification types', () => {
      const typeNotifications = [
        { id: 1, title: 'Info', message: 'Info message', type: 'info', read: false },
        { id: 2, title: 'Success', message: 'Success message', type: 'success', read: false },
        { id: 3, title: 'Warning', message: 'Warning message', type: 'warning', read: false },
        { id: 4, title: 'Error', message: 'Error message', type: 'error', read: false }
      ];

      render(
        <NotificationPanel
          notifications={typeNotifications}
          onMarkAsRead={jest.fn()}
          onDismiss={jest.fn()}
        />
      );

      expect(screen.getAllByTestId(/notification-/)).toHaveLength(4);
      expect(screen.getByTestId('notification-0')).toHaveTextContent('info');
      expect(screen.getByTestId('notification-1')).toHaveTextContent('success');
      expect(screen.getByTestId('notification-2')).toHaveTextContent('warning');
      expect(screen.getByTestId('notification-3')).toHaveTextContent('error');
    });
  });

  describe('Dashboard Integration', () => {
    test('components work together in dashboard layout', () => {
      const DashboardLayout = ({ children }) => (
        <div data-testid="dashboard-layout">
          <div data-testid="sidebar">Sidebar</div>
          <div data-testid="main-content">
            <div data-testid="header">Header</div>
            <div data-testid="dashboard-content">
              {children}
            </div>
          </div>
        </div>
      );

      render(
        <DashboardLayout>
          <StatsCard title="Test Stat" value="100" />
          <ChartComponent data={mockChartData} type="line" />
          <ActivityFeed activities={mockActivities} loading={false} />
          <NotificationPanel notifications={mockNotifications} />
        </DashboardLayout>
      );

      expect(screen.getByTestId('dashboard-layout')).toBeInTheDocument();
      expect(screen.getByTestId('sidebar')).toBeInTheDocument();
      expect(screen.getByTestId('main-content')).toBeInTheDocument();
      expect(screen.getByTestId('header')).toBeInTheDocument();
      expect(screen.getByTestId('dashboard-content')).toBeInTheDocument();
      expect(screen.getByTestId('stats-card')).toBeInTheDocument();
      expect(screen.getByTestId('chart-component')).toBeInTheDocument();
      expect(screen.getByTestId('activity-feed')).toBeInTheDocument();
      expect(screen.getByTestId('notification-panel')).toBeInTheDocument();
    });

    test('handles responsive behavior', () => {
      // Test mobile viewport
      Object.defineProperty(window, 'innerWidth', {
        writable: true,
        configurable: true,
        value: 375,
      });

      render(
        <div>
          <StatsCard title="Mobile Test" value="100" />
          <ChartComponent data={mockChartData} type="line" height={200} />
        </div>
      );

      expect(screen.getByTestId('stats-card')).toBeInTheDocument();
      expect(screen.getByTestId('chart-component')).toBeInTheDocument();
      expect(screen.getByTestId('chart-height')).toHaveTextContent('200');
    });
  });

  describe('Performance Considerations', () => {
    test('handles large datasets efficiently', () => {
      const largeDataset = Array.from({ length: 1000 }, (_, i) => ({
        month: `Month ${i}`,
        value: Math.floor(Math.random() * 1000)
      }));

      const startTime = performance.now();
      render(<ChartComponent data={largeDataset} type="line" />);
      const endTime = performance.now();

      // Should render within 100ms for 1000 data points
      expect(endTime - startTime).toBeLessThan(100);
      expect(screen.getByTestId('chart-data-points')).toHaveTextContent('1000');
    });

    test('debounces rapid interactions', () => {
      jest.useFakeTimers();

      const mockOnChange = jest.fn();
      const { rerender } = render(
        <StatsCard
          title="Debounce Test"
          value="100"
          onChange={mockOnChange}
        />
      );

      // Simulate rapid changes
      for (let i = 0; i < 10; i++) {
        rerender(
          <StatsCard
            title="Debounce Test"
            value={i.toString()}
            onChange={mockOnChange}
          />
        );
      }

      // Fast-forward timer
      act(() => {
        jest.advanceTimersByTime(500);
      });

      // Should only call once after debouncing
      expect(mockOnChange).toHaveBeenCalledTimes(1);

      jest.useRealTimers();
    });
  });

  describe('Error Handling', () => {
    test('handles missing props gracefully', () => {
      render(<StatsCard />);

      expect(screen.getByTestId('stats-card')).toBeInTheDocument();
      expect(screen.getByTestId('card-title')).toBeInTheDocument();
      expect(screen.getByTestId('card-value')).toBeInTheDocument();
    });

    test('handles invalid data types', () => {
      const invalidData = [
        { month: 'Jan', value: 'invalid' },
        { month: 'Feb', value: null },
        { month: 'Mar', value: undefined }
      ];

      render(<ChartComponent data={invalidData} type="line" />);

      expect(screen.getByTestId('chart-component')).toBeInTheDocument();
      expect(screen.getByTestId('chart-data-points')).toHaveTextContent('3');
    });
  });
});