"""Test cases for the models of the ``documents`` app"""

import os
import shutil

from mock import patch

from django.conf import settings

from constance.test import override_config
from factory.django import FileField

from mtp.apps.common.tests.test_cases import MTPTestCase, WithMediaOnDisk

from ..constants import ACTIVITY_TYPE_CHOICES
from ..factories import DocumentFactory
from ..models import Document

from .test_cases import DocumentTestCase


class DocumentModelTestCase(DocumentTestCase, WithMediaOnDisk):
    """Test cases for the ``Document`` model"""

    def create_document(self, file_extension):
        """Shortcut method to create a document with the given extension"""

        return DocumentFactory(
            trainee_document_file=FileField(filename='file.%s' % file_extension),
            trainer_document_file=FileField(filename='file.%s' % file_extension)
        )

    def test_is_video(self):
        """Test the ``is_video`` property"""

        for file_extension in Document.DOCUMENT_TABLE.keys():
            document = self.create_document(file_extension)
            with self.subTest(file_extension=file_extension):
                if file_extension in ['mov', 'm4v', 'mp4']:
                    self.assertTrue(
                        document.is_video
                    )
                else:
                    self.assertFalse(
                        document.is_video
                    )

    def test_compatibility_serialize(self):
        """Test the ``compatibility_serialize`` method"""

        for file_extension in Document.DOCUMENT_TABLE.keys():

            # New document with the ``file_extension``
            document = self.create_document(file_extension)

            # Serialize the document
            serialized_document = document.compatibility_serialize()

            # Test the properties of the serialized document
            with self.subTest(file_extension=file_extension):
                if file_extension in ['mov', 'm4v', 'mp4']:
                    # The document is a "video" type
                    self.assertEqual(
                        'video',
                        serialized_document[0]['type']
                    )
                    # We got the right preview
                    self.assertEqual(
                        'video-preview.png',
                        serialized_document[0]['preview']
                    )
                    # The document is not cached
                    self.assertFalse(serialized_document[0]['cached'])
                else:
                    # We got the right preview
                    self.assertEqual(
                        'document-preview.png',
                        serialized_document[0]['preview']
                    )
                    # The document is cached
                    self.assertTrue(serialized_document[0]['cached'])

                # We got the correct "file_name" fields
                self.assertEqual(
                    serialized_document[0]['file_name'],
                    '%s.%s' % (document.trainee_document_file_hash,
                               document.trainee_document_file.name.split('.')[-1])
                )
                self.assertEqual(
                    serialized_document[1]['file_name'],
                    '%s-annoted.%s' % (document.trainee_document_file_hash,
                                       document.trainee_document_file.name.split('.')[-1])
                )

                # Check `needs_approval` is False by default
                self.assertFalse(serialized_document[0]['needs_approval'])

    def test_media_files(self):
        """Test that the media file property should return the paths of the media files"""

        self.assertEqual(
            self.document.media_files,
            [
                self.document.trainee_document_file.name,
                self.document.trainer_document_file.name
            ]
        )

    def test_activity_type(self):
        """Test that activity type is the good if it's `is_video` or `trainer_only`"""

        # Test for video document
        video = self.create_document('mov')
        self.assertEqual(video.activity_type, ACTIVITY_TYPE_CHOICES.VIDEO)

        # Test for document
        self.assertEqual(self.document.activity_type, ACTIVITY_TYPE_CHOICES.DOCUMENT)

        # Test for document only visible by trainer
        document_trainer = DocumentFactory(trainer_only=True)
        self.assertEqual(
            document_trainer.activity_type,
            ACTIVITY_TYPE_CHOICES.DOCUMENT_TRAINER
        )


class GetPdfImagesAbsolutePathMethodTestCase(DocumentTestCase):
    """Test case for the ``get_pdf_images_absolute_path`` method on the ``Document`` model"""

    def test_get_pdf_images_absolute_path_should_output_the_right_path(self):
        """Test that the ``get_pdf_images_absolute_path`` correctly work"""

        images_absolute_path = os.path.normpath(
            os.path.join(
                settings.MEDIA_ROOT,
                settings.UPLOAD_TO[self.document._meta.object_name],
                self.document.PDF_IMAGES_FOLDER_PREFIX + self.document.trainee_document_file_hash,
            )
        )

        self.assertEqual(
            self.document.pdf_images_absolute_path,
            images_absolute_path
        )


@override_config(IMAGES_FROM_PDF_ACTIVATE=False)
class GenerateImagesFromPDFMethodTestCase(MTPTestCase, WithMediaOnDisk):
    """Test case for the ``generate_images_from_pdf`` method on the ``Document`` model"""

    @classmethod
    def setUpClass(cls):
        """Build the pdf to images folder path to use"""

        super().setUpClass()

        cls.images_path = cls.document.pdf_images_absolute_path

    @classmethod
    def setUpTestData(cls):
        """Get the path for generated images"""

        super().setUpTestData()

        # Create a new document with a "pdf" file attached
        cls.document = DocumentFactory(
            trainee_document_file=FileField(filename='file.pdf')
        )

    def tearDown(self):
        """Remove file path and its content."""

        super().tearDown()

        # We remove folder and its path
        shutil.rmtree(self.images_path, ignore_errors=True)

    @override_config(IMAGES_FROM_PDF_ACTIVATE=False)
    def test_is_generate_images_from_pdf_needed_when_setting_is_false(self):
        """Test ``is_generate_images_from_pdf_needed`` method when generation is deactivated"""

        needed, reason = self.document.is_generate_images_from_pdf_needed()

        self.assertFalse(needed)
        self.assertIsInstance(reason, ValueError)

    @override_config(IMAGES_FROM_PDF_ACTIVATE=True)
    def test_is_generate_images_from_pdf_needed_when_setting_is_true(self):
        """Test ``is_generate_images_from_pdf_needed`` method when generation is activated"""

        needed, reason = self.document.is_generate_images_from_pdf_needed()

        self.assertTrue(needed)
        self.assertIsNone(reason)

    @override_config(IMAGES_FROM_PDF_ACTIVATE=True)
    def test_is_generate_images_from_pdf_needed_when_folder_exists(self):
        """Test ``is_generate_images_from_pdf_needed`` method when images folder exists"""

        os.makedirs(self.images_path)

        needed, reason = self.document.is_generate_images_from_pdf_needed()

        self.assertFalse(needed)
        self.assertIsInstance(reason, FileExistsError)

    @override_config(IMAGES_FROM_PDF_ACTIVATE=True)
    def test_is_generate_images_from_pdf_needed_when_folder_not_exists(self):
        """Test ``is_generate_images_from_pdf_needed`` method when images folder don't exists"""

        needed, reason = self.document.is_generate_images_from_pdf_needed()

        self.assertTrue(needed)
        self.assertIsNone(reason)

    @override_config(IMAGES_FROM_PDF_ACTIVATE=True)
    def test_generate_images_from_pdf_not_async_should_call_generate_images_from_pdf(self):
        """Test that the ``generate_images_from_pdf`` method works in synchronous mode."""

        # We'll only check if the ``generate_images_from_pdf`` function is called
        with patch.object(Document, 'generate_images_from_pdf') as patched:
            self.document.save()

        # We should have one call to ``Document.generate_images_from_pdf``
        patched.assert_called_once_with(
            async=True
        )

    def test_save_document_should_not_call_generate_images_from_pdf_when_wrong_type(self):
        """Test that the save method of the document should not call the generation."""

        # Replace the document with a wrong type
        self.document = DocumentFactory(
            trainee_document_file=FileField(filename='file.notapdf')
        )

        with patch.object(Document, 'generate_images_from_pdf') as patched:
            self.document.save()

        patched.assert_not_called()

    def test_save_document_should_not_call_generate_images_from_pdf(self):
        """Test that the save method of the document should not call the generation."""

        with patch.object(Document, 'generate_images_from_pdf') as patched:
            self.document.save(export_pdf_to_images=False)

        patched.assert_not_called()
