from django.core.exceptions import ImproperlyConfigured

import pytest

from rest_polymorphic.serializers import PolymorphicSerializer

from tests.models import BlogBase, BlogOne, BlogTwo
from tests.serializers import BlogPolymorphicSerializer

pytestmark = pytest.mark.django_db


class TestPolymorphicSerializer:

    def test_model_serializer_mapping_is_none(self):
        class EmptyPolymorphicSerializer(PolymorphicSerializer):
            pass

        with pytest.raises(ImproperlyConfigured) as excinfo:
            EmptyPolymorphicSerializer()

        assert str(excinfo.value) == (
            '`EmptyPolymorphicSerializer` is missing a '
            '`EmptyPolymorphicSerializer.model_serializer_mapping` attribute'
        )

    def test_resource_type_field_name_is_not_string(self, mocker):
        class NotStringPolymorphicSerializer(PolymorphicSerializer):
            model_serializer_mapping = mocker.MagicMock
            resource_type_field_name = 1

        with pytest.raises(ImproperlyConfigured) as excinfo:
            NotStringPolymorphicSerializer()

        assert str(excinfo.value) == (
            '`NotStringPolymorphicSerializer.resource_type_field_name` must '
            'be a string'
        )

    def test_each_serializer_has_context(self, mocker):
        context = mocker.MagicMock()
        serializer = BlogPolymorphicSerializer(context=context)
        for inner_serializer in serializer.model_serializer_mapping.values():
            assert inner_serializer.context == context

    def test_serialize(self):
        instance = BlogBase.objects.create(name='blog', slug='blog')
        serializer = BlogPolymorphicSerializer(instance)
        assert serializer.data == {
            'name': 'blog',
            'slug': 'blog',
            'resourcetype': 'BlogBase',
        }

    def test_deserialize(self):
        data = {
            'name': 'blog',
            'slug': 'blog',
            'resourcetype': 'BlogBase',
        }
        serializers = BlogPolymorphicSerializer(data=data)
        assert serializers.is_valid()
        assert serializers.data == data

    def test_deserialize_with_invalid_resourcetype(self):
        data = {
            'name': 'blog',
            'resourcetype': 'Invalid',
        }
        serializers = BlogPolymorphicSerializer(data=data)
        assert not serializers.is_valid()

    def test_create(self):
        data = [
            {
                'name': 'a',
                'slug': 'a',
                'resourcetype': 'BlogBase'
            },
            {
                'name': 'b',
                'slug': 'b',
                'info': 'info',
                'resourcetype': 'BlogOne'
            },
            {
                'name': 'c',
                'slug': 'c',
                'resourcetype': 'BlogTwo'
            },
        ]
        serializer = BlogPolymorphicSerializer(data=data, many=True)
        assert serializer.is_valid()

        instances = serializer.save()
        assert len(instances) == 3
        assert [item.name for item in instances] == ['a', 'b', 'c']

        assert BlogBase.objects.count() == 3
        assert BlogBase.objects.instance_of(BlogOne).count() == 1
        assert BlogBase.objects.instance_of(BlogTwo).count() == 1

        assert serializer.data == data

    def test_update(self):
        instance = BlogBase.objects.create(name='blog', slug='blog')
        data = {
            'name': 'new-blog',
            'slug': 'blog',
            'resourcetype': 'BlogBase'
        }

        serializer = BlogPolymorphicSerializer(instance, data=data)
        assert serializer.is_valid()

        serializer.save()
        assert instance.name == 'new-blog'
        assert instance.slug == 'blog'

    def test_partial_update(self):
        instance = BlogBase.objects.create(name='blog', slug='blog')
        data = {
            'name': 'new-blog',
            'resourcetype': 'BlogBase'
        }

        serializer = BlogPolymorphicSerializer(
            instance, data=data, partial=True
        )
        assert serializer.is_valid()

        serializer.save()
        assert instance.name == 'new-blog'
        assert instance.slug == 'blog'

    def test_object_validators_are_applied(self):
        data = {
            'name': 'test-blog',
            'slug': 'test-blog-slug',
            'info': 'test-blog-info',
            'about': 'test-blog-about',
            'resourcetype': 'BlogThree'
        }
        serializer = BlogPolymorphicSerializer(data=data)
        assert serializer.is_valid()
        serializer.save()

        data['slug'] = 'test-blog-slug-new'
        duplicate = BlogPolymorphicSerializer(data=data)
        
        assert not duplicate.is_valid()
        assert 'non_field_errors' in duplicate.errors
        err = duplicate.errors['non_field_errors']

        assert err == ['The fields info, about must make a unique set.']