"""
Unit tests for notification views.
"""
from unittest.mock import Mock, patch, MagicMock
from datetime import datetime
import pytest


class TestNotificationViewSet:
    """Unit tests for the NotificationViewSet."""

    def test_get_queryset_filters_by_user(self):
        """Test get_queryset returns only current user's notifications."""
        from smoothschedule.communication.notifications.views import NotificationViewSet

        # Arrange
        mock_user = Mock()
        mock_user.id = 1

        mock_request = Mock()
        mock_request.user = mock_user

        viewset = NotificationViewSet()
        viewset.request = mock_request

        # Act
        with patch('smoothschedule.communication.notifications.views.Notification') as mock_model:
            mock_queryset = Mock()
            mock_model.objects.filter.return_value = mock_queryset

            result = viewset.get_queryset()

            # Assert
            mock_model.objects.filter.assert_called_once_with(recipient=mock_user)
            assert result == mock_queryset

    def test_list_returns_all_notifications_by_default(self):
        """Test list action returns all notifications without filters."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/notifications/')

        mock_user = Mock()
        mock_user.id = 1
        django_request.user = mock_user

        # Wrap in DRF Request to get query_params
        request = Request(django_request)

        viewset = NotificationViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Create mock notifications
        mock_notifications = [
            Mock(id=1, verb='test1', read=False),
            Mock(id=2, verb='test2', read=True),
        ]

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            with patch.object(viewset, 'get_serializer') as mock_serializer:
                mock_qs = Mock()
                mock_qs.__getitem__ = Mock(return_value=mock_notifications)
                mock_get_qs.return_value = mock_qs

                mock_serializer.return_value.data = [
                    {'id': 1, 'verb': 'test1'},
                    {'id': 2, 'verb': 'test2'},
                ]

                response = viewset.list(request)

        # Assert
        assert response.status_code == 200
        assert len(response.data) == 2

    def test_list_filters_by_read_status_true(self):
        """Test list action filters notifications by read=true."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/notifications/?read=true')

        mock_user = Mock()
        django_request.user = mock_user

        # Wrap in DRF Request
        request = Request(django_request)

        viewset = NotificationViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            with patch.object(viewset, 'get_serializer') as mock_serializer:
                mock_qs = Mock()
                mock_filtered_qs = Mock()
                mock_filtered_qs.__getitem__ = Mock(return_value=[])
                mock_qs.filter.return_value = mock_filtered_qs
                mock_get_qs.return_value = mock_qs

                mock_serializer.return_value.data = []

                response = viewset.list(request)

        # Assert
        mock_qs.filter.assert_called_once_with(read=True)

    def test_list_filters_by_read_status_false(self):
        """Test list action filters notifications by read=false."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/notifications/?read=false')

        mock_user = Mock()
        django_request.user = mock_user

        # Wrap in DRF Request
        request = Request(django_request)

        viewset = NotificationViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            with patch.object(viewset, 'get_serializer') as mock_serializer:
                mock_qs = Mock()
                mock_filtered_qs = Mock()
                mock_filtered_qs.__getitem__ = Mock(return_value=[])
                mock_qs.filter.return_value = mock_filtered_qs
                mock_get_qs.return_value = mock_qs

                mock_serializer.return_value.data = []

                response = viewset.list(request)

        # Assert
        mock_qs.filter.assert_called_once_with(read=False)

    def test_list_respects_limit_parameter(self):
        """Test list action respects limit query parameter."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/notifications/?limit=10')

        mock_user = Mock()
        django_request.user = mock_user

        # Wrap in DRF Request
        request = Request(django_request)

        viewset = NotificationViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            with patch.object(viewset, 'get_serializer') as mock_serializer:
                mock_qs = Mock()
                mock_qs.__getitem__ = Mock(return_value=[])
                mock_get_qs.return_value = mock_qs

                mock_serializer.return_value.data = []

                response = viewset.list(request)

        # Assert
        mock_qs.__getitem__.assert_called_once()
        # Verify slicing with [:10]
        call_args = mock_qs.__getitem__.call_args[0][0]
        assert call_args == slice(None, 10, None)

    def test_list_uses_default_limit_50(self):
        """Test list action uses default limit of 50."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/notifications/')

        mock_user = Mock()
        django_request.user = mock_user

        # Wrap in DRF Request
        request = Request(django_request)

        viewset = NotificationViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            with patch.object(viewset, 'get_serializer') as mock_serializer:
                mock_qs = Mock()
                mock_qs.__getitem__ = Mock(return_value=[])
                mock_get_qs.return_value = mock_qs

                mock_serializer.return_value.data = []

                response = viewset.list(request)

        # Assert
        call_args = mock_qs.__getitem__.call_args[0][0]
        assert call_args == slice(None, 50, None)

    def test_unread_count_returns_correct_count(self):
        """Test unread_count action returns count of unread notifications."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/notifications/unread_count/')

        mock_user = Mock()
        request.user = mock_user

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            mock_qs = Mock()
            mock_filtered_qs = Mock()
            mock_filtered_qs.count.return_value = 5
            mock_qs.filter.return_value = mock_filtered_qs
            mock_get_qs.return_value = mock_qs

            response = viewset.unread_count(request)

        # Assert
        mock_qs.filter.assert_called_once_with(read=False)
        assert response.status_code == 200
        assert response.data == {'count': 5}

    def test_unread_count_returns_zero_when_no_unread(self):
        """Test unread_count returns 0 when no unread notifications."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/notifications/unread_count/')

        mock_user = Mock()
        request.user = mock_user

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            mock_qs = Mock()
            mock_filtered_qs = Mock()
            mock_filtered_qs.count.return_value = 0
            mock_qs.filter.return_value = mock_filtered_qs
            mock_get_qs.return_value = mock_qs

            response = viewset.unread_count(request)

        # Assert
        assert response.data == {'count': 0}

    def test_mark_read_updates_single_notification(self):
        """Test mark_read action marks single notification as read."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/notifications/1/mark_read/')

        mock_user = Mock()
        request.user = mock_user

        mock_notification = Mock()
        mock_notification.read = False
        mock_notification.save = Mock()

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_object', return_value=mock_notification):
            response = viewset.mark_read(request, pk=1)

        # Assert
        assert mock_notification.read is True
        mock_notification.save.assert_called_once_with(update_fields=['read'])
        assert response.status_code == 200
        assert response.data == {'status': 'marked as read'}

    def test_mark_read_only_saves_read_field(self):
        """Test mark_read uses update_fields to save only read field."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/notifications/1/mark_read/')

        mock_notification = Mock()
        mock_notification.save = Mock()

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_object', return_value=mock_notification):
            viewset.mark_read(request, pk=1)

        # Assert
        mock_notification.save.assert_called_once_with(update_fields=['read'])

    def test_mark_all_read_updates_all_unread_notifications(self):
        """Test mark_all_read marks all unread notifications as read."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/notifications/mark_all_read/')

        mock_user = Mock()
        request.user = mock_user

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            mock_qs = Mock()
            mock_filtered_qs = Mock()
            mock_filtered_qs.update.return_value = 3
            mock_qs.filter.return_value = mock_filtered_qs
            mock_get_qs.return_value = mock_qs

            response = viewset.mark_all_read(request)

        # Assert
        mock_qs.filter.assert_called_once_with(read=False)
        mock_filtered_qs.update.assert_called_once_with(read=True)
        assert response.status_code == 200
        assert response.data == {'status': 'marked 3 notifications as read'}

    def test_mark_all_read_returns_zero_when_none_unread(self):
        """Test mark_all_read returns 0 when no unread notifications."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/notifications/mark_all_read/')

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            mock_qs = Mock()
            mock_filtered_qs = Mock()
            mock_filtered_qs.update.return_value = 0
            mock_qs.filter.return_value = mock_filtered_qs
            mock_get_qs.return_value = mock_qs

            response = viewset.mark_all_read(request)

        # Assert
        assert response.data == {'status': 'marked 0 notifications as read'}

    def test_clear_all_deletes_read_notifications(self):
        """Test clear_all deletes all read notifications."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.delete('/api/notifications/clear_all/')

        mock_user = Mock()
        request.user = mock_user

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            mock_qs = Mock()
            mock_filtered_qs = Mock()
            mock_filtered_qs.delete.return_value = (5, {'notifications.Notification': 5})
            mock_qs.filter.return_value = mock_filtered_qs
            mock_get_qs.return_value = mock_qs

            response = viewset.clear_all(request)

        # Assert
        mock_qs.filter.assert_called_once_with(read=True)
        mock_filtered_qs.delete.assert_called_once()
        assert response.status_code == 200
        assert response.data == {'status': 'deleted 5 notifications'}

    def test_clear_all_returns_zero_when_none_deleted(self):
        """Test clear_all returns 0 when no read notifications to delete."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.delete('/api/notifications/clear_all/')

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            mock_qs = Mock()
            mock_filtered_qs = Mock()
            mock_filtered_qs.delete.return_value = (0, {})
            mock_qs.filter.return_value = mock_filtered_qs
            mock_get_qs.return_value = mock_qs

            response = viewset.clear_all(request)

        # Assert
        assert response.data == {'status': 'deleted 0 notifications'}

    def test_clear_all_only_deletes_read_notifications(self):
        """Test clear_all filters for read=True before deleting."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.test import APIRequestFactory

        # Arrange
        factory = APIRequestFactory()
        request = factory.delete('/api/notifications/clear_all/')

        viewset = NotificationViewSet()
        viewset.request = request

        # Act
        with patch.object(viewset, 'get_queryset') as mock_get_qs:
            mock_qs = Mock()
            mock_filtered_qs = Mock()
            mock_filtered_qs.delete.return_value = (0, {})
            mock_qs.filter.return_value = mock_filtered_qs
            mock_get_qs.return_value = mock_qs

            viewset.clear_all(request)

        # Assert
        # Verify it filters for read=True, not read=False
        mock_qs.filter.assert_called_once_with(read=True)

    def test_viewset_permission_classes(self):
        """Test viewset requires authentication."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from rest_framework.permissions import IsAuthenticated

        assert IsAuthenticated in NotificationViewSet.permission_classes

    def test_viewset_serializer_class(self):
        """Test viewset uses correct serializer."""
        from smoothschedule.communication.notifications.views import NotificationViewSet
        from smoothschedule.communication.notifications.serializers import NotificationSerializer

        assert NotificationViewSet.serializer_class == NotificationSerializer

    def test_unread_count_action_configuration(self):
        """Test unread_count is configured as custom action."""
        from smoothschedule.communication.notifications.views import NotificationViewSet

        # Verify the action decorator was applied
        method = getattr(NotificationViewSet, 'unread_count')
        assert hasattr(method, 'mapping')
        assert 'get' in method.mapping

    def test_mark_read_action_configuration(self):
        """Test mark_read is configured as detail action."""
        from smoothschedule.communication.notifications.views import NotificationViewSet

        # Verify the action decorator was applied
        method = getattr(NotificationViewSet, 'mark_read')
        assert hasattr(method, 'mapping')
        assert 'post' in method.mapping

    def test_mark_all_read_action_configuration(self):
        """Test mark_all_read is configured as list action."""
        from smoothschedule.communication.notifications.views import NotificationViewSet

        # Verify the action decorator was applied
        method = getattr(NotificationViewSet, 'mark_all_read')
        assert hasattr(method, 'mapping')
        assert 'post' in method.mapping

    def test_clear_all_action_configuration(self):
        """Test clear_all is configured as list action with delete method."""
        from smoothschedule.communication.notifications.views import NotificationViewSet

        # Verify the action decorator was applied
        method = getattr(NotificationViewSet, 'clear_all')
        assert hasattr(method, 'mapping')
        assert 'delete' in method.mapping
