"""Test the ``hashable`` app."""

from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.test import override_settings, TestCase

from .hashable_testapp.models import Model1, Model2, Model3, Model4


# Use a storage backend that stores file in memory to gain speed and avoid polluting the disk.
@override_settings(DEFAULT_STORAGE='inmemorystorage.storage.InMemoryStorage')
class HashableTestCase(TestCase):
    """Test case for the ``hashable`` app."""

    @staticmethod
    def set_file(obj, field_name, file_name, content, save=True):
        """Utility to set a file field.

        Parameters
        ----------
        obj : model instance
            The model instance holding the file to set.
        field_name : str
            The name of the file field to set on ``obj``.
        file_name : str
            The name of the file to create.
        content : str
            The content of the file to create.
        save : bool
            Set it to ``True`` (the default) to save the instance (``obj``) after having set
            the field.

        """

        # Create a file
        default_storage.save(file_name, ContentFile(content))

        # Set its path to the field
        setattr(obj, field_name, file_name)

        if save:
            obj.save()

    def test_full_default_for_non_nullable_field(self):
        """Test full default configuration with a non-nullable model."""

        instance = Model1()
        self.set_file(instance, 'non_nullable_file', 'content1.txt', 'content1', save=True)

        # md5('content1').hexdigest() => '7e55db001d319a94b0b713529a756623'
        self.assertEqual(instance.non_nullable_hash, '7e55db001d319a94b0b713529a756623')

    def test_full_default_for_nullable_field(self):
        """Test full default configuration with a nullable model."""

        instance = Model2()

        # Saving the model without setting ``nullable_file`` should fail
        with self.assertRaises(ValueError):
            instance.save()

        # But filling it will work
        self.set_file(instance, 'nullable_file', 'content2.txt', 'content2', save=True)
        # md5('content2').hexdigest() => 'eea670f4ac941df71a3b5f268ebe3eac'
        self.assertEqual(instance.nullable_hash, 'eea670f4ac941df71a3b5f268ebe3eac')

    def test_with_default_decorator(self):
        """Test full default configuration with a nullable model."""

        instance = Model3()

        # Saving without values
        instance.save()

        self.assertEqual(instance.nullable_hash1, 'default')
        # For ``hash2``, tt's 'default' because the hash function is ``with_default`` without
        # defining hash function, so the default one is used, and it's also a ``with_default``
        # which use "default" as default value (and it doesn't raise so we don't have "foo")
        self.assertEqual(instance.nullable_hash2, 'default')
        self.assertEqual(instance.nullable_hash3, 'bar')

        # Now set values to ensure we don't have the default anymore

        self.set_file(instance, 'nullable_file1', 'content3.txt', 'content3', save=True)
        # md5('content3').hexdigest() => 'c96310e55d9677b978eae0dada47642c'
        self.assertEqual(instance.nullable_hash1, 'c96310e55d9677b978eae0dada47642c')
        self.assertEqual(instance.nullable_hash2, 'default')
        self.assertEqual(instance.nullable_hash3, 'bar')

        self.set_file(instance, 'nullable_file2', 'content4.txt', 'content4', save=True)
        self.assertEqual(instance.nullable_hash1, 'c96310e55d9677b978eae0dada47642c')
        # md5('content4').hexdigest() => 'e22e815a116e7bdea9a59a87e63464a1'
        self.assertEqual(instance.nullable_hash2, 'e22e815a116e7bdea9a59a87e63464a1')
        self.assertEqual(instance.nullable_hash3, 'bar')

        self.set_file(instance, 'nullable_file3', 'content5.txt', 'content5', save=True)
        self.assertEqual(instance.nullable_hash1, 'c96310e55d9677b978eae0dada47642c')
        self.assertEqual(instance.nullable_hash2, 'e22e815a116e7bdea9a59a87e63464a1')
        # md5('content5').hexdigest() => '9fd5be8a5f880452b9d0c2cfced3e76a'
        self.assertEqual(instance.nullable_hash3, '9fd5be8a5f880452b9d0c2cfced3e76a')

    def test_hash_methods(self):
        """Test that possibilities to change the hash method."""

        instance = Model4()
        self.set_file(instance, 'file1', 'content6.txt', 'content6', save=False)
        self.set_file(instance, 'file2', 'content7.txt', 'content7', save=False)

        instance.save()

        self.assertEqual(instance.hash1, 'default hash for file1')
        self.assertEqual(instance.hash2, 'specific hash for file2')
