"""This module holds the base test cases for the ``sync`` app"""

from datetime import datetime
from io import BytesIO
from zipfile import ZipFile, ZIP_LZMA

from django.conf import settings
from django.core import serializers
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.utils.timezone import utc

from django_children_models_dependencies.manager import ChildrenDependenciesManager
from waffle.models import Switch

import mtp

from ..models import Syncable


class DownloadWaffleBaseTestCase(TestCase):
    """Base test case for testing downloading waffle. Creates two switches.

    Attributes
    ----------
    switch1 : Switch
        A switch object name 'test1', NOT active.
    switch2 : Switch
        A switch object name 'test2', active.

    """

    @classmethod
    def setUpTestData(cls):
        """Create sample switches."""

        super().setUpTestData()

        cls.switch1 = Switch.objects.create(
            name='test1',
            active=False,
            created=datetime(2015, 6, 2, 12, 12, 12, tzinfo=utc),
            modified=datetime(2015, 6, 3, 13, 13, 13, tzinfo=utc),
        )
        cls.switch2 = Switch.objects.create(
            name='test2',
            active=True,
            created=datetime(2015, 6, 4, 14, 14, 14, tzinfo=utc),
            modified=datetime(2015, 6, 5, 15, 15, 15, tzinfo=utc),
        )


class SyncableDependencyTestCase(TestCase):
    """Test that the ``Syncable`` dependency tree can be computed."""

    def test_get_dependencies(self):
        """Test that the ``get_children_dependencies`` method doesn't raise an exception."""

        try:
            ChildrenDependenciesManager.get_children_dependencies(Syncable)
        except Exception as exception:  # pylint: disable=broad-except
            self.fail('Syncable dependency tree could not be generated:\n%s' % exception)


class BaseSyncUploadTestCase:
    """Base class to use when testing the sync upload endpoint"""

    @classmethod
    def setUpClass(cls):
        """Prepare the url to be called by the request client"""

        super().setUpClass()

        cls.url = reverse('sync:upload')

    def build_zip_file(self, data):
        """Helper method to build a Zipfile object containing the data.json

        Parameters
        ----------
        data: str
            The data to write into the "data.json" file into the archive

        Returns
        -------
        BytesIO instance
            The buffer containing the archive

        """

        zip_buffer = BytesIO()
        zip_file = ZipFile(zip_buffer, 'w', ZIP_LZMA)
        zip_file.setpassword(bytes(settings.SYNC_ZIP_PASSWORD, encoding='utf-8'))
        zip_file.writestr('data.json', data)
        zip_file.close()

        zip_buffer.seek(0)

        return zip_buffer

    def post_objects(self, objects):
        """Helper that posts some objects to the upload endpoint.

        Parameters
        ----------
        objects : list(models.Model)
            List of model instances to upload

        Returns
        -------
        Response
            The response of the post request from the client

        """

        json_stream = serializers.serialize('json', objects)

        zip_buffer = self.build_zip_file(json_stream)

        params = {
            'mac': "666",
            'version': mtp.__version__,
            'data_archive': zip_buffer
        }

        # Sync the data
        return self.client.post(self.url, params)
