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 TaskList from '../TaskList';
import axios from 'axios';

// Mock the contexts
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(),
};

// Mock axios
jest.mock('axios');

describe('TaskList Component', () => {
  const mockTasks = [
    {
      _id: 'task1',
      description: 'Clean up the street',
      type: 'cleaning',
      status: 'pending',
      pointsAwarded: 10,
      street: { streetId: 'street1', name: 'Main Street' },
      createdAt: '2023-01-01T00:00:00.000Z',
    },
    {
      _id: 'task2',
      description: 'Fix pothole',
      type: 'maintenance',
      status: 'completed',
      pointsAwarded: 15,
      street: { streetId: 'street2', name: 'Oak Street' },
      completedBy: { userId: 'user123', name: 'Test User' },
      completedAt: '2023-01-02T00:00:00.000Z',
      createdAt: '2023-01-01T00:00:00.000Z',
    },
  ];

  beforeEach(() => {
    jest.clearAllMocks();
    
    // Mock axios.get to return tasks
    axios.get.mockResolvedValue({ data: mockTasks });
    
    // Mock axios.put to return updated task
    axios.put.mockResolvedValue({ data: { ...mockTasks[0], status: 'completed' } });
  });

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

  it('renders task list correctly', async () => {
    renderTaskList();
    
    await waitFor(() => {
      expect(screen.getByText('Clean up the street')).toBeInTheDocument();
      expect(screen.getByText('Fix pothole')).toBeInTheDocument();
    });
  });

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

  it('displays task status correctly', async () => {
    renderTaskList();
    
    await waitFor(() => {
      const pendingTask = screen.getByText('Clean up the street').closest('[data-status="pending"]');
      const completedTask = screen.getByText('Fix pothole').closest('[data-status="completed"]');
      
      expect(pendingTask).toBeInTheDocument();
      expect(completedTask).toBeInTheDocument();
    });
  });

  it('displays task points', async () => {
    renderTaskList();
    
    await waitFor(() => {
      expect(screen.getByText('10 points')).toBeInTheDocument();
      expect(screen.getByText('15 points')).toBeInTheDocument();
    });
  });

  it('displays street information', async () => {
    renderTaskList();
    
    await waitFor(() => {
      expect(screen.getByText('Main Street')).toBeInTheDocument();
      expect(screen.getByText('Oak Street')).toBeInTheDocument();
    });
  });

  it('handles task completion', async () => {
    renderTaskList();
    
    await waitFor(() => {
      const completeButton = screen.getAllByText('Complete Task')[0];
      expect(completeButton).toBeInTheDocument();
    });

    // Click complete button
    fireEvent.click(completeButton);

    // Verify axios.put was called
    await waitFor(() => {
      expect(axios.put).toHaveBeenCalledWith('/api/tasks/task1', {
        status: 'completed'
      });
    });
  });

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

  it('filters tasks by status', async () => {
    renderTaskList();
    
    await waitFor(() => {
      // Find filter buttons
      const filterButtons = screen.getAllByRole('button');
      const pendingFilter = filterButtons.find(btn => btn.textContent.includes('Pending'));
      const completedFilter = filterButtons.find(btn => btn.textContent.includes('Completed'));
      
      expect(pendingFilter).toBeInTheDocument();
      expect(completedFilter).toBeInTheDocument();
    });
  });

  it('filters tasks by type', async () => {
    renderTaskList();
    
    await waitFor(() => {
      // Find type filter buttons
      const filterButtons = screen.getAllByRole('button');
      const cleaningFilter = filterButtons.find(btn => btn.textContent.includes('Cleaning'));
      const maintenanceFilter = filterButtons.find(btn => btn.textContent.includes('Maintenance'));
      
      expect(cleaningFilter).toBeInTheDocument();
      expect(maintenanceFilter).toBeInTheDocument();
    });
  });

  it('displays empty state when no tasks', async () => {
    // Mock empty response
    axios.get.mockResolvedValue({ data: [] });
    
    renderTaskList();
    
    await waitFor(() => {
      expect(screen.getByText('No tasks found')).toBeInTheDocument();
    });
  });

  it('shows task creation form', async () => {
    renderTaskList();
    
    await waitFor(() => {
      expect(screen.getByText('Create New Task')).toBeInTheDocument();
      expect(screen.getByPlaceholderText('Task description')).toBeInTheDocument();
    });
  });

  it('validates task creation form', async () => {
    renderTaskList();
    
    await waitFor(() => {
      const createButton = screen.getByText('Create Task');
      const descriptionInput = screen.getByPlaceholderText('Task description');
      
      // Try to create task without description
      fireEvent.click(createButton);
      
      expect(screen.getByText('Description is required')).toBeInTheDocument();
    });
  });

  it('creates new task successfully', async () => {
    // Mock axios.post for task creation
    axios.post.mockResolvedValue({ data: { ...mockTasks[0], _id: 'task3' } });
    
    renderTaskList();
    
    await waitFor(() => {
      const createButton = screen.getByText('Create Task');
      const descriptionInput = screen.getByPlaceholderText('Task description');
      const typeSelect = screen.getByDisplayValue('cleaning');
      
      fireEvent.change(descriptionInput, { target: { value: 'New test task' } });
      fireEvent.change(typeSelect, { target: { value: 'maintenance' } });
      fireEvent.click(createButton);
      
      expect(axios.post).toHaveBeenCalledWith('/api/tasks', {
        description: 'New test task',
        type: 'maintenance'
      });
    });
  });

  it('handles real-time updates', async () => {
    const { on } = mockSSEContext;
    
    renderTaskList();
    
    await waitFor(() => {
      // Simulate receiving a task update via SSE
      const sseCallback = on.mock.calls[0][1];
      const taskUpdateData = {
        type: 'task_update',
        data: { ...mockTasks[0], status: 'completed' }
      };
      
      sseCallback(taskUpdateData);
      
      // Verify the task list updates with new data
      expect(screen.getByText('Clean up the street')).toBeInTheDocument();
    });
  });

  it('displays task priority indicators', async () => {
    renderTaskList();
    
    await waitFor(() => {
      // Look for priority indicators
      const priorityElements = screen.getAllByTestId('task-priority');
      expect(priorityElements.length).toBeGreaterThan(0);
    });
  });

  it('shows task statistics', async () => {
    renderTaskList();
    
    await waitFor(() => {
      expect(screen.getByText('Total Tasks: 2')).toBeInTheDocument();
      expect(screen.getByText('Completed: 1')).toBeInTheDocument();
      expect(screen.getByText('Pending: 1')).toBeInTheDocument();
    });
  });

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

  it('searches tasks', async () => {
    renderTaskList();
    
    await waitFor(() => {
      const searchInput = screen.getByPlaceholderText('Search tasks...');
      expect(searchInput).toBeInTheDocument();
      
      // Test search functionality
      fireEvent.change(searchInput, { target: { value: 'clean' } });
      
      // Should filter tasks to show only cleaning tasks
      expect(screen.getByText('Clean up the street')).toBeInTheDocument();
      expect(screen.queryByText('Fix pothole')).not.toBeInTheDocument();
    });
  });
});