import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import { AuthContext } from '../../context/AuthContext';
import { SSEContext } from '../../context/SSEContext';
import Events from '../Events';
import axios from 'axios';

// Mocks
const mockAuthContext = {
  auth: { isAuthenticated: true, loading: false, user: { id: 'user123', name: 'Test User' } },
  login: jest.fn(),
  logout: jest.fn(),
};

const mockSSEContext = {
  connected: true,
  notifications: [],
  on: jest.fn(),
  off: jest.fn(),
  subscribe: jest.fn().mockResolvedValue({ subscribed: [] }),
  unsubscribe: jest.fn().mockResolvedValue({ unsubscribed: [] }),
  clearNotification: jest.fn(),
  clearAllNotifications: jest.fn(),
};

jest.mock('axios');

describe('Events Component', () => {
  const mockEvents = [
    {
      _id: 'event1',
      title: 'Community Cleanup Day',
      description: 'Join us for a community cleanup event',
      date: '2023-06-15T10:00:00.000Z',
      location: 'Central Park',
      participants: [],
      participantsCount: 0,
      status: 'upcoming',
      createdAt: '2023-01-01T00:00:00.000Z',
    },
    {
      _id: 'event2',
      title: 'Street Maintenance Workshop',
      description: 'Learn proper street maintenance techniques',
      date: '2023-06-20T14:00:00.000Z',
      location: 'Community Center',
      participants: [
        { userId: 'user123', name: 'Test User', joinedAt: '2023-01-01T00:00:00.000Z' }
      ],
      participantsCount: 1,
      status: 'ongoing',
      createdAt: '2023-01-02T00:00:00.000Z',
    },
    {
      _id: 'event3',
      title: 'Completed Event',
      description: 'This event has already finished',
      date: '2023-01-01T00:00:00.000Z',
      location: 'City Hall',
      participants: [
        { userId: 'user123', name: 'Test User', joinedAt: '2023-01-01T00:00:00.000Z' },
        { userId: 'user456', name: 'Other User', joinedAt: '2023-01-01T00:00:00.000Z' }
      ],
      participantsCount: 2,
      status: 'completed',
      createdAt: '2022-12-01T00:00:00.000Z',
    },
  ];

  beforeEach(() => {
    jest.clearAllMocks();
    
    // Mock axios.get to return events
    axios.get.mockResolvedValue({ data: mockEvents });
    
    // Mock axios.post for event creation
    axios.post.mockResolvedValue({ data: { ...mockEvents[0], _id: 'event4' } });
    
    // Mock axios.put for event joining
    axios.put.mockResolvedValue({ data: { ...mockEvents[1], participants: [...mockEvents[1].participants, { userId: 'user123', name: 'Test User', joinedAt: '2023-01-01T00:00:00.000Z' }] } });
  });

  const renderEvents = () => {
    return render(
      <BrowserRouter>
        <AuthContext.Provider value={mockAuthContext}>
          <SSEContext.Provider value={mockSSEContext}>
            <Events />
          </SSEContext.Provider>
        </AuthContext.Provider>
      </BrowserRouter>
    );
  };

  it('renders events list correctly', async () => {
    renderEvents();
    
    await waitFor(() => {
      expect(screen.getByText('Community Cleanup Day')).toBeInTheDocument();
      expect(screen.getByText('Street Maintenance Workshop')).toBeInTheDocument();
      expect(screen.getByText('Completed Event')).toBeInTheDocument();
    });
  });

  it('shows loading state initially', () => {
    // Mock axios to delay response
    axios.get.mockImplementation(() => new Promise(resolve => setTimeout(() => resolve({ data: [] }), 100)));
    
    renderEvents();
    
    expect(screen.getByText('Loading events...')).toBeInTheDocument();
  });

  it('displays event status correctly', async () => {
    renderEvents();
    
    await waitFor(() => {
      const upcomingEvent = screen.getByText('Community Cleanup Day').closest('[data-status="upcoming"]');
      const ongoingEvent = screen.getByText('Street Maintenance Workshop').closest('[data-status="ongoing"]');
      const completedEvent = screen.getByText('Completed Event').closest('[data-status="completed"]');
      
      expect(upcomingEvent).toBeInTheDocument();
      expect(ongoingEvent).toBeInTheDocument();
      expect(completedEvent).toBeInTheDocument();
    });
  });

  it('displays participant count', async () => {
    renderEvents();
    
    await waitFor(() => {
      expect(screen.getByText('0 participants')).toBeInTheDocument();
      expect(screen.getByText('1 participant')).toBeInTheDocument();
      expect(screen.getByText('2 participants')).toBeInTheDocument();
    });
  });

  it('displays event dates correctly', async () => {
    renderEvents();
    
    await waitFor(() => {
      expect(screen.getByText('June 15, 2023')).toBeInTheDocument();
      expect(screen.getByText('June 20, 2023')).toBeInTheDocument();
    });
  });

  it('displays event locations', async () => {
    renderEvents();
    
    await waitFor(() => {
      expect(screen.getByText('Central Park')).toBeInTheDocument();
      expect(screen.getByText('Community Center')).toBeInTheDocument();
      expect(screen.getByText('City Hall')).toBeInTheDocument();
    });
  });

  it('shows event creation form', async () => {
    renderEvents();
    
    await waitFor(() => {
      expect(screen.getByText('Create New Event')).toBeInTheDocument();
      expect(screen.getByPlaceholderText('Event title')).toBeInTheDocument();
      expect(screen.getByPlaceholderText('Event description')).toBeInTheDocument();
    });
  });

  it('validates event creation form', async () => {
    renderEvents();
    
    await waitFor(() => {
      const createButton = screen.getByText('Create Event');
      const titleInput = screen.getByPlaceholderText('Event title');
      
      expect(createButton).toBeInTheDocument();
      expect(titleInput).toBeInTheDocument();
    });
  });

  it('creates new event successfully', async () => {
    renderEvents();
    
    await waitFor(() => {
      const createButton = screen.getByText('Create Event');
      const titleInput = screen.getByPlaceholderText('Event title');
      const descriptionInput = screen.getByPlaceholderText('Event description');
      const dateInput = screen.getByDisplayValue('2023-06-15');
      const locationInput = screen.getByPlaceholderText('Event location');
      
      expect(createButton).toBeInTheDocument();
      expect(titleInput).toBeInTheDocument();
      expect(descriptionInput).toBeInTheDocument();
      expect(dateInput).toBeInTheDocument();
      expect(locationInput).toBeInTheDocument();
    });

    // Fill out form
    fireEvent.change(titleInput, { target: { value: 'New Test Event' } });
    fireEvent.change(descriptionInput, { target: { value: 'Test event description' } });
    fireEvent.change(dateInput, { target: { value: '2023-07-01' } });
    fireEvent.change(locationInput, { target: { value: 'Test Location' } });
    
    // Submit form
    fireEvent.click(createButton);
    
    // Verify axios.post was called
    await waitFor(() => {
      expect(axios.post).toHaveBeenCalledWith('/api/events', {
        title: 'New Test Event',
        description: 'Test event description',
        date: '2023-07-01',
        location: 'Test Location'
      });
    });
  });

  it('handles event joining', async () => {
    renderEvents();
    
    await waitFor(() => {
      const joinButtons = screen.getAllByText('Join Event');
      const firstJoinButton = joinButtons[0];
      
      expect(firstJoinButton).toBeInTheDocument();
    });
  });

  it('updates participant count when joining event', async () => {
    renderEvents();
    
    await waitFor(() => {
      const joinButtons = screen.getAllByText('Join Event');
      const firstJoinButton = joinButtons[0];
      
      fireEvent.click(firstJoinButton);
      
      expect(axios.put).toHaveBeenCalledWith('/api/events/event1/join');
    });
  });

  it('shows error message when API fails', async () => {
    // Mock axios.get to throw error
    axios.get.mockRejectedValue(new Error('Network error'));
    
    renderEvents();
    
    await waitFor(() => {
      expect(screen.getByText(/Failed to load events/)).toBeInTheDocument();
    });
  });

  it('displays empty state when no events', async () => {
    // Mock empty response
    axios.get.mockResolvedValue({ data: [] });
    
    renderEvents();
    
    await waitFor(() => {
      expect(screen.getByText('No upcoming events')).toBeInTheDocument();
      expect(screen.getByText('Be the first to create one!')).toBeInTheDocument();
    });
  });

  it('filters events by status', async () => {
    renderEvents();
    
    await waitFor(() => {
      // Look for filter buttons
      const filterButtons = screen.getAllByRole('button');
      const upcomingFilter = filterButtons.find(btn => btn.textContent.includes('Upcoming'));
      const completedFilter = filterButtons.find(btn => btn.textContent.includes('Completed'));
      
      expect(upcomingFilter).toBeInTheDocument();
      expect(completedFilter).toBeInTheDocument();
    });
  });

  it('searches events', async () => {
    renderEvents();
    
    await waitFor(() => {
      const searchInput = screen.getByPlaceholderText('Search events...');
      expect(searchInput).toBeInTheDocument();
    });
  });

  it('shows event details', async () => {
    renderEvents();
    
    await waitFor(() => {
      const eventCards = screen.getAllByTestId('event-card');
      expect(eventCards.length).toBeGreaterThan(0);
      
      // Check first event card
      const firstCard = eventCards[0];
      expect(firstCard).toHaveTextContent('Community Cleanup Day');
    });
  });

  it('handles real-time updates', async () => {
    const { on } = mockSSEContext;
    
    renderEvents();
    
    await waitFor(() => {
      // Simulate receiving a new event via SSE
      const sseCallback = on.mock.calls[0][1];
      const newEventData = {
        type: 'new_event',
        event: { ...mockEvents[0], _id: 'event5' }
      };
      
      sseCallback(newEventData);
      
      // Verify new event appears in the list
      expect(screen.getByText('Community Cleanup Day')).toBeInTheDocument();
    });
  });

  it('displays user\'s joined events', async () => {
    renderEvents();
    
    await waitFor(() => {
      // Look for "My Events" section
      const myEventsSection = screen.getByText('My Events');
      expect(myEventsSection).toBeInTheDocument();
      
      // Should show events user has joined
      expect(screen.getByText('Street Maintenance Workshop')).toBeInTheDocument();
    });
  });

  it('handles event cancellation', async () => {
    renderEvents();
    
    await waitFor(() => {
      const cancelButtons = screen.getAllByText('Cancel');
      const firstCancelButton = cancelButtons[0];
      
      expect(firstCancelButton).toBeInTheDocument();
    });
  });

  it('shows event statistics', async () => {
    renderEvents();
    
    await waitFor(() => {
      expect(screen.getByText('Total Events: 3')).toBeInTheDocument();
      expect(screen.getByText('Upcoming: 1')).toBeInTheDocument();
      expect(screen.getByText('Ongoing: 1')).toBeInTheDocument();
      expect(screen.getByText('Completed: 1')).toBeInTheDocument();
    });
  });

  it('handles pagination', async () => {
    renderEvents();
    
    await waitFor(() => {
      // Look for pagination controls
      expect(screen.getByText('Next')).toBeInTheDocument();
      expect(screen.getByText('Previous')).toBeInTheDocument();
    });
  });
});