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 SocialFeed from '../SocialFeed';
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('SocialFeed Component', () => {
  const mockPosts = [
    {
      _id: 'post1',
      content: 'Just cleaned up Main Street! 🧹',
      type: 'text',
      user: { userId: 'user123', name: 'Test User', profilePicture: 'avatar.jpg' },
      likes: [],
      likesCount: 0,
      comments: [],
      commentsCount: 0,
      createdAt: '2023-01-01T00:00:00.000Z',
    },
    {
      _id: 'post2',
      content: 'Beautiful sunset on Oak Street 🌅',
      type: 'image',
      imageUrl: 'https://example.com/sunset.jpg',
      cloudinaryPublicId: 'sunset_123',
      user: { userId: 'user456', name: 'Other User', profilePicture: 'avatar2.jpg' },
      likes: ['user123', 'user789'],
      likesCount: 2,
      comments: [
        {
          _id: 'comment1',
          content: 'Great work!',
          user: { userId: 'user789', name: 'Another User' },
          createdAt: '2023-01-01T01:00:00.000Z',
        }
      ],
      commentsCount: 1,
      createdAt: '2023-01-01T12:00:00.000Z',
    },
  ];

  beforeEach(() => {
    jest.clearAllMocks();
    
    // Mock axios.get to return posts
    axios.get.mockResolvedValue({ data: mockPosts });
    
    // Mock axios.post for creating posts
    axios.post.mockResolvedValue({ data: { ...mockPosts[0], _id: 'post3' } });
    
    // Mock axios.put for liking posts
    axios.put.mockResolvedValue({ data: { ...mockPosts[0], likes: ['user123'], likesCount: 1 } });
  });

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

  it('renders social feed correctly', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      expect(screen.getByText('Just cleaned up Main Street! 🧹')).toBeInTheDocument();
      expect(screen.getByText('Beautiful sunset on Oak Street 🌅')).toBeInTheDocument();
    });
  });

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

  it('displays user information for posts', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      expect(screen.getByText('Test User')).toBeInTheDocument();
      expect(screen.getByText('Other User')).toBeInTheDocument();
    });
  });

  it('displays post timestamps', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      expect(screen.getByText(/Jan 1, 2023/)).toBeInTheDocument();
    });
  });

  it('handles post creation', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      const createButton = screen.getByText('Create Post');
      const contentInput = screen.getByPlaceholderText('What\'s on your mind?');
      
      expect(createButton).toBeInTheDocument();
      expect(contentInput).toBeInTheDocument();
    });
  });

  it('creates new post successfully', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      const createButton = screen.getByText('Create Post');
      const contentInput = screen.getByPlaceholderText('What\'s on your mind?');
      
      fireEvent.change(contentInput, { target: { value: 'New test post' } });
      fireEvent.click(createButton);
      
      expect(axios.post).toHaveBeenCalledWith('/api/posts', {
        content: 'New test post'
      });
    });
  });

  it('validates post creation form', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      const createButton = screen.getByText('Create Post');
      
      // Try to create post without content
      fireEvent.click(createButton);
      
      expect(screen.getByText('Content is required')).toBeInTheDocument();
    });
  });

  it('handles post liking', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      const likeButtons = screen.getAllByLabelText('Like');
      const firstLikeButton = likeButtons[0];
      
      expect(firstLikeButton).toBeInTheDocument();
      
      // Click like button
      fireEvent.click(firstLikeButton);
      
      expect(axios.put).toHaveBeenCalledWith('/api/posts/post1/like');
    });
  });

  it('updates like count correctly', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      // Initial like count should be 2
      expect(screen.getByText('2 likes')).toBeInTheDocument();
    });
  });

  it('displays comments correctly', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      expect(screen.getByText('Great work!')).toBeInTheDocument();
      expect(screen.getByText('Another User')).toBeInTheDocument();
    });
  });

  it('shows comment count', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      expect(screen.getByText('1 comment')).toBeInTheDocument();
      expect(screen.getByText('0 comments')).toBeInTheDocument();
    });
  });

  it('handles comment submission', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      const commentInput = screen.getByPlaceholderText('Add a comment...');
      const submitButton = screen.getByText('Post Comment');
      
      expect(commentInput).toBeInTheDocument();
      expect(submitButton).toBeInTheDocument();
    });
  });

  it('submits comment successfully', async () => {
    // Mock axios.post for comment creation
    axios.post.mockResolvedValue({ data: { _id: 'comment2', content: 'Test comment' } });
    
    renderSocialFeed();
    
    await waitFor(() => {
      const commentInput = screen.getByPlaceholderText('Add a comment...');
      const submitButton = screen.getByText('Post Comment');
      
      fireEvent.change(commentInput, { target: { value: 'Test comment' } });
      fireEvent.click(submitButton);
      
      expect(axios.post).toHaveBeenCalledWith('/api/posts/post1/comments', {
        content: 'Test comment'
      });
    });
  });

  it('displays image posts correctly', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      const postImages = screen.getAllByAltText('Post image');
      expect(postImages.length).toBeGreaterThan(0);
    });
  });

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

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

  it('handles real-time updates', async () => {
    const { on } = mockSSEContext;
    
    renderSocialFeed();
    
    await waitFor(() => {
      // Simulate receiving a new post via SSE
      const sseCallback = on.mock.calls[0][1];
      const newPostData = {
        type: 'new_post',
        data: { ...mockPosts[0], _id: 'post3', content: 'New real-time post!' }
      };
      
      sseCallback(newPostData);
      
      // Verify the new post appears in the feed
      expect(screen.getByText('New real-time post!')).toBeInTheDocument();
    });
  });

  it('filters posts by type', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      // Look for filter buttons
      const filterButtons = screen.getAllByRole('button');
      const allFilter = filterButtons.find(btn => btn.textContent.includes('All'));
      const textFilter = filterButtons.find(btn => btn.textContent.includes('Text'));
      const imageFilter = filterButtons.find(btn => btn.textContent.includes('Images'));
      
      expect(allFilter).toBeInTheDocument();
      expect(textFilter).toBeInTheDocument();
      expect(imageFilter).toBeInTheDocument();
    });
  });

  it('searches posts', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      const searchInput = screen.getByPlaceholderText('Search posts...');
      expect(searchInput).toBeInTheDocument();
      
      // Test search functionality
      fireEvent.change(searchInput, { target: { value: 'cleaned' } });
      
      // Should filter posts to show only relevant content
      expect(screen.getByText('Just cleaned up Main Street! 🧹')).toBeInTheDocument();
    });
  });

  it('shows post engagement statistics', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      // Look for engagement stats
      expect(screen.getByText('Total Posts: 2')).toBeInTheDocument();
      expect(screen.getByText('Total Likes: 2')).toBeInTheDocument();
      expect(screen.getByText('Total Comments: 1')).toBeInTheDocument();
    });
  });

  it('handles infinite scroll', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      // Look for load more indicator
      expect(screen.getByText('Load more posts')).toBeInTheDocument();
    });
  });

  it('displays user avatars', async () => {
    renderSocialFeed();
    
    await waitFor(() => {
      const avatars = screen.getAllByAltText('User avatar');
      expect(avatars.length).toBeGreaterThan(0);
    });
  });
});