"""This module holds models tests for the ``photostories`` app."""

from io import BytesIO
from zipfile import ZipFile

from mock import patch

from mtp.apps.common.tests.test_utils import create_fake_image_file

from .test_cases import PhotostoryTestCase

from ..apiv2.serializers import PhotoSerializer, StripSerializer
from ..factories import PhotoFactory, StripFactory
from ..models import Photo


class PhotostoryModelTestCase(PhotostoryTestCase):
    """Test the ``Photostory`` model."""

    def test_create_archive(self):
        """Test the ``create_archive`` method."""

        archive = self.photostory.create_archive()

        # The archive content should exist
        self.assertIsNotNone(archive.data)

        # Test the content of the zip file
        with BytesIO(archive.data) as content_file:
            with ZipFile(content_file, 'r') as zipped_file:
                # It should be valid
                self.assertIsNone(zipped_file.testzip())
                self.assertIn('%s/1.jpg' % self.strip.name, zipped_file.namelist())

                # Count the number of photo that the archive should contain
                nb_photo = Photo.objects.filter(strip__photostory=self.photostory).count()
                # The archive should contain as many file as photos
                self.assertEqual(len(zipped_file.namelist()), nb_photo)

        # We create a new strip in an other session
        # but still in the same activity
        strip = StripFactory(photostory=self.photostory)
        # then a new photo in this new strip
        PhotoFactory(strip=strip)

        # Get the zip file
        archive = self.photostory.create_archive()

        # Test the content of the zip file
        with BytesIO(archive.data) as content_file:
            with ZipFile(content_file, 'r') as zipped_file:
                # We should have now 1 more photo in the archive
                self.assertEqual(len(zipped_file.namelist()), nb_photo + 1)

    def test_create_archive_by_session(self):
        """Test the ``create_archive`` method filtering by session."""

        archive = self.photostory.create_archive(session=self.training_session)

        # The archive content should exist
        self.assertIsNotNone(archive.data)

        # Test the content of the zip file
        with BytesIO(archive.data) as content_file:
            with ZipFile(content_file, 'r') as zipped_file:
                # It should be valid
                self.assertIsNone(zipped_file.testzip())
                self.assertIn('%s/1.jpg' % self.strip.name, zipped_file.namelist())

                # Count the number of photo that the archive should contain
                nb_photo = Photo.objects.filter(strip__photostory=self.photostory).filter(
                    strip__session=self.training_session).count()
                # The archive should contain as many file as photos
                self.assertEqual(len(zipped_file.namelist()), nb_photo)

        # We create a new strip in an other session
        # but still in the same activity
        strip = StripFactory(photostory=self.photostory)
        # then a new photo in this new strip
        PhotoFactory(strip=strip)

        # Get the zip file
        archive = self.photostory.create_archive(session=self.training_session)

        # Test the content of the zip file
        with BytesIO(archive.data) as content_file:
            with ZipFile(content_file, 'r') as zipped_file:
                # We should have still the same number of photo
                self.assertEqual(len(zipped_file.namelist()), nb_photo)


class StripModelTestCase(PhotostoryTestCase):
    """Test the ``Strip`` model."""

    @classmethod
    def setUpClass(cls):
        """Prepare the tests data."""

        super().setUpClass()

        cls.strip = cls.strips[0]

    def test_add_photo(self):
        """Test the ``add_photo`` method."""

        self.strip.add_photo(create_fake_image_file())

        # Now, we should have 3 + 1 photos in this strip
        self.assertEqual(self.strip.photos.count(), 4)

    def test_has_photos(self):
        """Test the ``has_photos`` method."""

        # This strip should have photos
        self.assertTrue(self.strip.has_photos())

        new_strip = StripFactory(
            photostory=self.photostory,
            session=self.training_session
        )

        # The new strip shouldn't have photos
        self.assertFalse(new_strip.has_photos())

    def test_publish(self):
        """Test the ``publish`` method."""

        self.assertEqual(self.strip.state, self.strip.STRIP_STATE.ONGOING)

        self.strip.publish()

        self.assertEqual(self.strip.state, self.strip.STRIP_STATE.PUBLISHED)

    def test_create_archive(self):
        """Test the ``create_archive`` method."""

        archive = self.strip.create_archive()

        # The archive content should exist
        self.assertIsNotNone(archive.data)

        # Test the content of the zip file
        with BytesIO(archive.data) as content_file:
            with ZipFile(content_file, 'r') as zipped_file:
                # We should have the same number of files than the number of photos
                self.assertEqual(len(zipped_file.namelist()), len(self.strip.photos.non_deleted()))

        # Now we add a photo then delete it
        photo = PhotoFactory(strip=self.strip)
        photo.remove()

        archive = self.strip.create_archive()

        # Test the content of the zip file
        with BytesIO(archive.data) as content_file:
            with ZipFile(content_file, 'r') as zipped_file:
                # We should have the same number of files than the number of non deleted photos
                self.assertEqual(len(zipped_file.namelist()), len(self.strip.photos.non_deleted()))

    def test_get_broker_topic_method_should_work(self):
        """Test that the ``get_broker_topic``should correctly return the broker topic"""

        self.assertEqual(
            self.strip.get_broker_topic(),
            '%s/photostories/%s/strips' % (
                self.strip.session_id,
                self.strip.photostory_id
            )
        )

    def test_get_serialized_representation_should_work(self):
        """Test that the ``get_serialized_representation`` should work"""

        self.assertEqual(
            self.strip.get_serialized_representation(),
            StripSerializer(self.strip).data
        )

    def test_creating_a_strip_should_post_to_the_broker(self):
        """Test that creating a new strip should post to the broker"""

        with patch('mtp.apps.broker_models_hooks.tasks.publish_to_broker.delay') as mocked:
            new_strip = StripFactory(
                photostory=self.photostory,
                session=self.training_session
            )

        mocked.assert_called_once_with(
            new_strip.get_serialized_representation(),
            new_strip.get_broker_topic(),
            'CREATE'
        )

    def test_updating_an_existing_strip_should_call_publish_to_broker(self):
        """Test that updating an existing strip should post to the broker"""

        with patch('mtp.apps.broker_models_hooks.tasks.publish_to_broker.delay') as mocked:
            self.strip.save()

        mocked.assert_called_once_with(
            self.strip.get_serialized_representation(),
            self.strip.get_broker_topic(),
            'UPDATE'
        )


class PhotoModelTestCase(PhotostoryTestCase):
    """Test the ``Photo`` model."""

    def test_sync_files(self):
        """Test the ``sync_files`` method."""
        fake_image = create_fake_image_file()
        photo = PhotoFactory(strip=self.strips[0], image=fake_image)

        self.assertEqual(
            photo.get_sync_files(),
            [photo.image.name]
        )

    def test_get_broker_topic_method_should_work(self):
        """Test that the ``get_broker_topic``should correctly return the broker topic"""

        self.assertEqual(
            self.photo.get_broker_topic(),
            '%s/photostories/%s/strips/%s/photos' % (
                self.photo.strip.session_id,
                self.photo.strip.photostory_id,
                self.photo.strip_id
            )
        )

    def test_get_serialized_representation_should_work(self):
        """Test that the ``get_serialized_representation`` should work"""

        self.assertEqual(
            self.photo.get_serialized_representation(),
            PhotoSerializer(self.photo).data
        )

    def test_creating_a_photo_should_post_to_the_broker(self):
        """Test that creating a new photo should post to the broker"""

        with patch('mtp.apps.broker_models_hooks.tasks.publish_to_broker.delay') as mocked:
            new_photo = PhotoFactory(strip=self.strip)

        mocked.assert_called_once_with(
            new_photo.get_serialized_representation(),
            new_photo.get_broker_topic(),
            'CREATE'
        )

    def test_updating_an_existing_photo_should_call_publish_to_broker(self):
        """Test that updating an existing photo should post to the broker"""

        with patch('mtp.apps.broker_models_hooks.tasks.publish_to_broker.delay') as mocked:
            self.photo.save()

        mocked.assert_called_once_with(
            self.photo.get_serialized_representation(),
            self.photo.get_broker_topic(),
            'UPDATE'
        )
