"""This module holds viewsets tests for ``api`` app."""

import json

from rest_framework import status, viewsets
from rest_framework.test import APIRequestFactory, APITestCase

from ..mixins import ListLiveModelMixin, SoftDestroyModelMixin

from .api_testapp.factories import (
    LiveModelFactory,
    NonSyncableModelFactory,
    SyncableModelFactory
)
from .api_testapp.models import (
    LiveModel,
    NonSyncableModel,
    SyncableModel
)
from .api_testapp.serializers import (
    LiveModelSerializer,
    NonSyncableModelSerializer,
    SyncableModelSerializer
)


class SoftDestroyModelMixinTestCase(APITestCase):
    """Test case for the ``SoftDestroyModelMixin`` mixin."""

    class SyncableModelViewSet(SoftDestroyModelMixin, viewsets.ModelViewSet):
        """The viewset for syncable instances."""

        queryset = SyncableModel.objects.non_deleted()
        serializer_class = SyncableModelSerializer

    class NonSyncableModelViewSet(SoftDestroyModelMixin, viewsets.ModelViewSet):
        """The viewset for non syncable instances."""

        queryset = NonSyncableModel.objects.all()
        serializer_class = NonSyncableModelSerializer

    @classmethod
    def setUpTestData(cls):
        """Create a request factory and instances to test."""

        super().setUpTestData()

        cls.factory = APIRequestFactory()
        cls.syncable_instance = SyncableModelFactory()
        cls.non_syncable_instance = NonSyncableModelFactory()

    def test_soft_delete_syncable_instance(self):
        """Test the soft deletion of syncable instance through an api call."""

        # The instance shouldn't be soft-deleted
        self.assertFalse(self.syncable_instance.is_deleted)

        request = self.factory.delete('/users/%s/' % self.syncable_instance.pk)
        view = self.SyncableModelViewSet.as_view({'delete': 'destroy'})

        response = view(request, pk=self.syncable_instance.pk)
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

        # The instance should be soft-deleted
        self.syncable_instance.refresh_from_db()
        self.assertTrue(self.syncable_instance.is_deleted)

    def test_delete_non_syncable_instance(self):
        """Test the deletion of non syncable instance through an api call."""

        request = self.factory.delete('/users/%s/' % self.non_syncable_instance.pk)
        view = self.NonSyncableModelViewSet.as_view({'delete': 'destroy'})

        response = view(request, pk=self.non_syncable_instance.pk)
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

        # The instance should be delete
        with self.assertRaises(NonSyncableModel.DoesNotExist):
            self.non_syncable_instance.refresh_from_db()


class ListLiveModelMixinTestCase(APITestCase):
    """Test case for the ``ListLiveModelMixin`` mixin."""

    class LiveModelViewSet(ListLiveModelMixin, viewsets.ModelViewSet):
        """The viewset for live instances."""

        queryset = LiveModel.objects.all()
        serializer_class = LiveModelSerializer

    @classmethod
    def setUpTestData(cls):
        """Create a request factory and instances to test."""

        super().setUpTestData()

        cls.factory = APIRequestFactory()
        cls.first_live_instance = LiveModelFactory(session_id=1)
        cls.second_live_instance = LiveModelFactory(session_id=2)
        cls.non_live_instance = LiveModelFactory(session_id=None)

    def test_call_view_without_session_parameter_should_raise(self):
        """Test that the view should raise if called without session parameter"""

        request = self.factory.get('/instances/')
        view = self.LiveModelViewSet.as_view({'get': 'list'})

        response = view(request).render()

        # Without session parameter, the view return an error
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(response.content.decode('utf-8'), '["Session parameter is mandatory"]')

    def test_call_view_with_session_parameter_should_filter(self):
        """Test that the view should filter the instances if called with session parameter"""

        request = self.factory.get('/instances/?session=1')
        view = self.LiveModelViewSet.as_view({'get': 'list'})

        response = view(request).render()
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        json_content = json.loads(response.content.decode('utf-8'))

        # We got two instances returned (the "second_live_instance" is excluded)...
        self.assertEqual(len(json_content), 2)

        # ...But 3 instances in total
        self.assertEqual(LiveModel.objects.count(), 3)
