"""This module holds serializers tests for ``photostories`` api."""

from mock import patch, PropertyMock

from rest_framework.exceptions import ParseError
from rest_framework.serializers import DateTimeField

from mtp.apps.common.tests.test_utils import create_fake_image_file
from mtp.apps.photostories.factories import StripFactory
from mtp.apps.photostories.tests.test_cases import PhotostoryTestCase

from ..serializers import PhotoSerializer, PhotostorySerializer, StripSerializer


class PhotoSerializerTestCase(PhotostoryTestCase):
    """Test the ``PhotoSerializer`` serializer."""

    def test_serializer_is_working_correctly(self):
        """Test that the serializer is working correctly"""

        serializer = PhotoSerializer(self.photo)

        self.assertEqual(
            serializer.data,
            {
                'id': self.photo.id,
                'image': self.photo.image.url,
                'sort_order': self.photo.sort_order,
                'strip': self.photo.strip_id,
                'created': DateTimeField().to_representation(self.photo.created),
                'modified':  DateTimeField().to_representation(self.photo.modified)
            }
        )

    def test_creation(self):
        """Test the ``create`` method."""

        with patch.object(PhotoSerializer, 'strip_id', new_callable=PropertyMock) as prop:
            prop.return_value = self.strip.id

            # Photo serializer with no explicit strip should use the url
            serializer = PhotoSerializer(
                data={
                    'image': create_fake_image_file()
                }
            )
            serializer.is_valid()

            photo = serializer.save()
            self.assertEqual(photo.strip_id, self.strip.id)

            # Photo serializer with explicit strip should use it
            new_strip = StripFactory()
            serializer = PhotoSerializer(
                data={
                    'image': create_fake_image_file(),
                    'strip': new_strip.id,
                }
            )
            serializer.is_valid()

            photo = serializer.save()
            self.assertEqual(photo.strip_id, new_strip.id)

            # Photo serializer with wrong data should raise error
            prop.return_value = '3e094465-3d17-4612-8214-84d2320b5a17'
            serializer = PhotoSerializer(
                data={
                    'image': create_fake_image_file(),
                }
            )
            with self.assertRaisesMessage(ParseError, 'The strip id in the url is wrong.'):
                serializer.is_valid()
                photo = serializer.save()


class StripSerializerTestCase(PhotostoryTestCase):
    """Test the ``StripSerializer`` serializer."""

    def test_creation(self):
        """Test the ``create`` method."""

        with patch.object(StripSerializer,
                          'photostory_id',
                          new_callable=PropertyMock,
                          return_value=self.photostory.id):
            # Strip serializer with no explicit photostory should use the url
            # We use a pure training activity here, the session can't be omitted
            serializer = StripSerializer(
                data={
                    'name': 'fake',
                    'session': self.training_session.id,
                }
            )
            serializer.is_valid()

            strip = serializer.save(owner=self.trainee)
            self.assertEqual(strip.photostory_id, self.photostory.id)
            self.assertEqual(strip.session_id, self.training_session.id)

            # Strip serializer with explicit photostory should use it
            # We use a live photostory here, the session can be omitted
            # because the photostory provides it
            serializer = StripSerializer(
                data={
                    'name': 'fake',
                    'photostory': self.photostory_live.id,
                }
            )
            serializer.is_valid()

            strip = serializer.save(owner=self.trainee)
            self.assertEqual(strip.photostory_id, self.photostory_live.id)
            self.assertEqual(strip.session_id, self.photostory_live.session_id)

        # We set a wrong photostory ID in URL
        with patch.object(StripSerializer,
                          'photostory_id',
                          new_callable=PropertyMock,
                          return_value='3e094465-3d17-4612-8214-84d2320b5a17'):
            # Strip serializer with wrong photostory should raise error
            serializer = StripSerializer(
                data={
                    'name': 'fake',
                }
            )
            with self.assertRaisesMessage(ParseError, 'The photostory id in url is wrong.'):
                serializer.is_valid()
                strip = serializer.save(owner=self.trainee)

        # We set an photostory ID in URL wich is not live, so without session linked
        with patch.object(StripSerializer,
                          'photostory_id',
                          new_callable=PropertyMock,
                          return_value=self.photostory.id):
            # Strip serializer with no session should raise error
            serializer = StripSerializer(
                data={
                    'name': 'fake',
                    'photostory': self.photostory.id,
                }
            )
            with self.assertRaisesMessage(ParseError, 'The `session` parameter is missing'):
                serializer.is_valid()
                strip = serializer.save(owner=self.trainee)


class PhotostorySerializerTestCase(PhotostoryTestCase):
    """Test the ``PhotostorySerializer`` serializer."""

    def test_serializer_is_working_correctly(self):
        """Test that the serializer is working correctly"""

        serializer = PhotostorySerializer(self.photostory)

        self.assertEqual(
            serializer.data,
            {
                'id': self.photostory.id,
                'name': self.photostory.name,
                'description': self.photostory.description,
                'activity_type': self.photostory.activity_type,
                'sort_order': self.photostory.sort_order,
                'session': self.photostory.session_id,
                'training': self.photostory.training_id
            }
        )

    def test_creation(self):
        """Test the ``create`` method."""

        # Photostory serializer with no session should save
        serializer = PhotostorySerializer(
            data={
                'training': self.training_session.training.id,
                'name': 'Test name',
                'description': 'Test description'
            }
        )
        serializer.is_valid()

        photostory = serializer.save()
        self.assertEqual(photostory.training.id, self.training_session.training.id)
        self.assertEqual(photostory.session, None)

        # Photostory serializer with no training but with session should save
        serializer = PhotostorySerializer(
            data={
                'session': self.training_session.id,
                'name': 'Test name',
                'description': 'Test description'
            }
        )
        serializer.is_valid()

        photostory = serializer.save()
        self.assertEqual(photostory.training, None)
        self.assertEqual(photostory.session.id, self.training_session.id)

        # Photostory serializer with session AND training sould not save
        serializer = PhotostorySerializer(
            data={
                'training': self.training_session.training.id,
                'session': self.training_session.id,
                'name': 'Test name',
                'description': 'Test description'
            }
        )
        serializer.is_valid()

        error_msg = 'The activity can not be linked to a training and to a session at the same time'
        with self.assertRaisesMessage(TypeError, error_msg):
            photostory = serializer.save()

        # Photostory serializer with NO session and NO training sould not save
        serializer = PhotostorySerializer(
            data={
                'training': None,
                'session': None,
                'name': 'Test name',
                'description': 'Test description'
            }
        )
        serializer.is_valid()

        error_msg = 'You cannot call `.save()` on a serializer with invalid data.'
        with self.assertRaisesMessage(AssertionError, error_msg):
            serializer.save()

        # Photostory serializer with sort order set, should not to be used
        serializer = PhotostorySerializer(
            data={
                'training': self.training_session.training.id,
                'name': 'Test name',
                'description': 'Test description',
                'sort_order': 1
            }
        )
        serializer.is_valid()

        photostory = serializer.save()
        self.assertNotEqual(photostory.sort_order, 1)
