"""This module holds the test cases for the managers of the ``media`` app"""

from operator import itemgetter
import os
import shutil
from mock import patch

from django.conf import settings
from django.test import override_settings
from django.test.testcases import TestCase

from mtp.apps.common.tests.test_cases import MTPTestCase, WithMediaOnDisk
from mtp.apps.documents.factories import DocumentFactory, NoteFactory
from mtp.apps.documents.models import Document

from mtp.apps.media.factories import (
    PdfMediaFactory,
    PresentationFactory,
    PresentationMediaFactory,
)
from mtp.apps.media.managers import DependencyMediaManager
from mtp.apps.media.models import (
    Note,
    Media,
    Presentation,
    PresentationMedia,
    Video
)


class DependencyPresentationMediaContentQuerySetTestCase(WithMediaOnDisk, TestCase):
    """Test case for the ``DependencyPresentationMediaContentsQuerySet`` QuerySet"""

    def test_get_media_for_presentation_should_return_the_media(self):
        """Test that the ``get_media_for_presentation`` should work correctly"""

        pdf_media = PdfMediaFactory()
        restricted_pdf_media = PdfMediaFactory()
        presentation = PresentationFactory()

        # The media is not yet associated with the presentation
        self.assertIsNone(
            PresentationMedia.objects.get_media_for_presentation(presentation)
        )

        PresentationMediaFactory(
            media=pdf_media,
            presentation=presentation
        )

        # Now we should be able to find the media
        self.assertEqual(
            PresentationMedia.objects.get_media_for_presentation(presentation),
            pdf_media
        )

        # No restricted media yet
        self.assertIsNone(
            PresentationMedia.objects.get_media_for_presentation(
                presentation,
                is_restricted=True
            )
        )

        PresentationMediaFactory(
            media=restricted_pdf_media,
            presentation=presentation,
            is_restricted=True
        )

        self.assertEqual(
            PresentationMedia.objects.get_media_for_presentation(
                presentation,
                is_restricted=True
            ),
            restricted_pdf_media
        )


class DependencyMediaManagerTestCase(WithMediaOnDisk, MTPTestCase):
    """Test case for the ``DependencyMediaManager`` Manager"""

    def setUp(self):
        """Create some ``Document``objects to migrate and store their associated values

        Notes
        -----
        We don't use ``setUpTestData``here, because we want the files associated to the
        objects below to be created for each test, because we might delete them.

        """

        super().setUp()

        # Create a new ``Document`` instance with a video file in it
        self.video_document = DocumentFactory(trainee_document_file__filename='test.mov')
        self.video_document_values = {
            'id': self.video_document.id,
            'name': self.video_document.name,
            'description': self.video_document.description,
            'is_video': True,
            'trainee_document_file_name': self.video_document.trainee_document_file.name,
            'trainee_document_file_hash': self.video_document.trainee_document_file_hash,
            'sort_order': self.video_document.sort_order,
            'training_id': self.video_document.training_id,
            'trainer_only': self.video_document.trainer_only,
            'is_deleted': self.video_document.is_deleted,
        }

        # Create a new ``Document`` instance with two pdf files in it
        self.pdf_document = DocumentFactory(
            trainee_document_file__filename='trainee.pdf',
            trainee_document_file__data=b'trainee',
            trainer_document_file__filename='trainer.pdf',
            trainer_document_file__data=b'trainer',
        )

        self.pdf_document_values = {
            'id': self.pdf_document.id,
            'name': self.pdf_document.name,
            'description': self.pdf_document.description,
            'is_video': False,
            'trainee_document_file_name': self.pdf_document.trainee_document_file.name,
            'trainee_document_file_hash': self.pdf_document.trainee_document_file_hash,
            'trainer_document_file_name': self.pdf_document.trainer_document_file.name,
            'trainer_document_file_hash': self.pdf_document.trainer_document_file_hash,
            'sort_order': self.pdf_document.sort_order,
            'training_id': self.pdf_document.training_id,
            'trainer_only': self.pdf_document.trainer_only,
            'is_deleted': self.pdf_document.is_deleted,
        }

        # Create one "free note"
        self.free_note = NoteFactory(
            user=self.trainee,
            session=self.training_session,
            text='Free note'
        )

        # Create one "document note"
        self.document_note = NoteFactory(
            user=self.trainee,
            session=self.training_session,
            text='document note',
            document=self.pdf_document,
            page=1
        )

    @staticmethod
    def get_file_absolute_path_for_field(field):
        """Get the file absolute path for the given file field

        Parameters
        ----------
        field: FileField
            The file field we want the absolute path of the file

        Returns
        -------
        str
            The absolute path of the file contained in the file field

        """

        return os.path.join(
            settings.MEDIA_ROOT,
            field.name
        )

    def test_get_values_for_document(self):
        """Test that the ``get_values_for_document`` method is working correctly"""

        # Get values for a valid document video
        values = Media.objects.get_values_for_document(self.video_document)
        self.assertEqual(self.video_document_values, values)

        # Get values for an invalid video files
        invalid_video = DocumentFactory(
            trainee_document_file__filename='test.avi',
            trainer_document_file=None  # default is a .dat file
        )
        invalid_values = Media.objects.get_values_for_document(invalid_video)
        self.assertFalse('trainee_document_file_name' in invalid_values)

        # Get values for a corrupt video files
        invalid_video = DocumentFactory(
            trainee_document_file__filename='test.mp4',
            trainer_document_file=None  # default is a .dat file
        )
        os.remove(
            self.get_file_absolute_path_for_field(invalid_video.trainee_document_file)
        )
        invalid_values = Media.objects.get_values_for_document(invalid_video)
        self.assertFalse('trainee_document_file_name' in invalid_values)

        # Get values for a valid document video
        values = Media.objects.get_values_for_document(self.video_document)
        self.assertEqual(self.video_document_values, values)

        # Get values without trainer pdf files
        invalid_document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file=None  # default is a .dat file
        )
        invalid_values = Media.objects.get_values_for_document(invalid_document)
        self.assertTrue('trainee_document_file_name' in invalid_values)
        self.assertFalse('trainer_document_file_name' in invalid_values)

        # Get values with invalid trainee pdf files
        invalid_document = DocumentFactory(
            trainee_document_file__filename='test.docx',
            trainer_document_file__filename='test.pdf',
        )
        invalid_values = Media.objects.get_values_for_document(invalid_document)
        self.assertFalse('trainee_document_file_name' in invalid_values)
        self.assertTrue('trainer_document_file_name' in invalid_values)

        # Get values with invalid trainer pdf files
        invalid_document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file__filename='test.docx',
        )
        invalid_values = Media.objects.get_values_for_document(invalid_document)
        self.assertFalse('trainer_document_file_name' in invalid_values)

        # Get values for a corrupt trainee pdf files
        invalid_document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file__filename='test.pdf',
        )
        os.remove(
            self.get_file_absolute_path_for_field(invalid_document.trainee_document_file)
        )
        invalid_values = Media.objects.get_values_for_document(invalid_document)
        self.assertFalse('trainee_document_file_name' in invalid_values)
        self.assertTrue('trainer_document_file_name' in invalid_values)

        # Get values for a corrupt trainer pdf files
        invalid_document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file__filename='test.pdf',
        )
        os.remove(
            self.get_file_absolute_path_for_field(invalid_document.trainer_document_file)
        )
        invalid_values = Media.objects.get_values_for_document(invalid_document)
        self.assertFalse('trainer_document_file_name' in invalid_values)

    def test_get_values_for_duplicated_document(self):
        """Test that the ``get_values_for_document`` for a duplicate doc is working correctly"""

        # Duplicate our document
        duplicated_document = self.pdf_document.duplicate_in_training(self.pdf_document.training)

        # Migrate the main document
        Media.objects.migrate_document_to_media(self.pdf_document)

        # The correct keys are set if we call the ``get_values_for_document`` on the duplicate
        duplicated_document_values = Media.objects.get_values_for_document(duplicated_document)
        self.assertTrue(duplicated_document_values['trainee_document_already_moved'])
        self.assertTrue(duplicated_document_values['trainer_document_already_moved'])

    def test_move_files_to_media_should_work(self):
        """Test that the ``move_files_to_media`` method is working correctly"""

        # I - We move document files
        values = Media.objects.move_files_to_media(self.pdf_document_values)

        # file name should be updated
        self.assertEqual(
            values['trainee_document_file_name'],
            os.path.join(
                'media_app/pdf',
                os.path.basename(self.pdf_document_values['trainee_document_file_name'])
            )
        )
        self.assertEqual(
            values['trainer_document_file_name'],
            os.path.join(
                'media_app/pdf',
                os.path.basename(self.pdf_document_values['trainer_document_file_name'])
            )
        )

        # II - we move corrupted trainee document files
        document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file__filename='test.pdf',
        )
        values = Media.objects.get_values_for_document(document)
        os.remove(
            self.get_file_absolute_path_for_field(document.trainee_document_file)
        )
        values = Media.objects.move_files_to_media(values)

        # files name should be updated
        self.assertFalse('trainee_document_file_name' in values)
        self.assertTrue('trainer_document_file_name' in values)

        # III - we move corrupted trainer document files
        document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file__filename='test.pdf',
        )
        values = Media.objects.get_values_for_document(document)
        os.remove(
            self.get_file_absolute_path_for_field(document.trainer_document_file)
        )
        values = Media.objects.move_files_to_media(values)

        # file name should be updated
        self.assertEqual(
            values['trainee_document_file_name'],
            os.path.join(
                'media_app/pdf',
                os.path.basename(document.trainee_document_file.name)
            )
        )
        self.assertFalse('trainer_document_file_name' in values)

        # IV - We move video file
        values = Media.objects.move_files_to_media(self.video_document_values)

        # file name should be updated
        self.assertEqual(
            values['trainee_document_file_name'],
            os.path.join(
                'media_app/video',
                os.path.basename(self.video_document_values['trainee_document_file_name'])
            )
        )

        # V - we move corrupted video file
        video = DocumentFactory(trainee_document_file__filename='test.mp4')
        values = Media.objects.get_values_for_document(video)
        os.remove(
            self.get_file_absolute_path_for_field(video.trainee_document_file)
        )
        values = Media.objects.move_files_to_media(values)

        # file name should be updated
        self.assertFalse('trainee_document_file_name' in values)

        # VI - We move images files
        images_src = os.path.join(
            settings.MEDIA_ROOT,
            'documents',
            'pdf_images_' + self.pdf_document_values['trainee_document_file_hash']
        ) # always the same hash in test
        images_dest = os.path.join(
            settings.MEDIA_ROOT,
            'media_app/pdf',
            'images_' + self.pdf_document_values['trainee_document_file_hash']
        ) # always the same hash in test
        if os.path.exists(images_dest):
            shutil.rmtree(images_dest)
        os.makedirs(images_src)
        Media.objects.move_files_to_media(self.pdf_document_values)

        # Image directory should be moved
        self.assertFalse(os.path.exists(images_src))
        self.assertTrue(os.path.isdir(images_dest))
        shutil.rmtree(images_dest)

        # VII - We not move images files when trainee files is corrupt
        document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file=None
        )
        os.makedirs(images_src)
        values = Media.objects.get_values_for_document(document)
        os.remove(
            self.get_file_absolute_path_for_field(document.trainee_document_file)
        )
        Media.objects.move_files_to_media(values)

        # Image directory should not be moved
        self.assertTrue(os.path.exists(images_src))
        self.assertFalse(os.path.isdir(images_dest))

    def test_move_files_to_media_should_work_on_duplicated_document(self):
        """Test that the ``move_files_to_media`` method should work for a duplicated document"""

        # Duplicate our document
        duplicated_document = self.pdf_document.duplicate_in_training(self.pdf_document.training)

        # Migrate the main document
        Media.objects.migrate_document_to_media(self.pdf_document)

        # Migrate our duplicated document
        with patch.object(DependencyMediaManager, 'move_file_to_media') as mocked:
            Media.objects.migrate_document_to_media(duplicated_document)

        # For the duplicated document, no moved files (the files are already moved)
        self.assertEqual(mocked.call_count, 0)

        # New document
        new_document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file='test.pdf'
        )

        # Duplicate
        new_duplicate = new_document.duplicate_in_training(new_document.training)

        # We drop the trainer file of our main document
        new_document.trainer_document_file = None
        new_document.save()

        # Migrate the parent document
        Media.objects.migrate_document_to_media(new_document)

        # Migrate the children
        with patch.object(DependencyMediaManager, 'move_file_to_media') as mocked:
            Media.objects.migrate_document_to_media(new_duplicate)

        # We should have one call to ``move_file_to_media``, since the trainer file
        # was not moved yet
        self.assertEqual(mocked.call_count, 1)

    def test_move_back_files_to_document_should_work(self):
        """Test that the ``move_back_files_to_document`` method is working correctly"""

        src_path = os.path.join(settings.MEDIA_ROOT, 'documents')
        dst_path = os.path.join(settings.MEDIA_ROOT, 'media_app/pdf')

        # src dir is empty
        shutil.rmtree(src_path)
        os.makedirs(src_path)
        # dest dir is empty
        shutil.rmtree(dst_path)
        os.makedirs(dst_path)

        # New document files
        document = DocumentFactory(
            trainee_document_file__filename='trainee.pdf',
            trainee_document_file__data=b'trainee',
            trainer_document_file__filename='trainer.pdf',
            trainer_document_file__data=b'trainer'
        )

        # Create images folder
        os.makedirs(
            os.path.join(
                src_path,
                'pdf_images_' + self.pdf_document_values['trainee_document_file_hash']
            )
        )
        os.makedirs(
            os.path.join(
                src_path,
                'pdf_images_' + self.pdf_document_values['trainer_document_file_hash']
            )
        )

        values = Media.objects.get_values_for_document(document)

        # src dir is not empty
        self.assertEqual(
            len(os.listdir(src_path)),
            4
        )

        # dest dir is still empty
        self.assertEqual(
            len(os.listdir(dst_path)),
            0
        )

        # We move document files
        Media.objects.move_files_to_media(values)

        # src dir is now empty
        self.assertEqual(
            len(os.listdir(src_path)),
            0
        )

        # dest dir is not empty
        self.assertEqual(
            len(os.listdir(dst_path)),
            4
        )

        # We move back document files
        Media.objects.move_back_files_to_document(values)

        # src dir is not empty
        self.assertEqual(
            len(os.listdir(src_path)),
            4
        )

        # dest dir is empty
        self.assertEqual(
            len(os.listdir(dst_path)),
            0
        )

    @override_settings(INSTANCE_IS_SLAVE=False)
    def test_migrate_document_to_video_media_on_master_should_work(self):
        """Test that the ``migrate_document_to_video_media`` method is working correctly"""

        # No ``Video`` objects yet
        self.assertEqual(Video.objects.count(), 0)

        # Original file exist
        src_file_path = self.get_file_absolute_path_for_field(
            self.video_document.trainee_document_file
        )
        self.assertTrue(os.path.isfile(src_file_path))

        # Migrate our ``Document``
        values = Media.objects.move_files_to_media(self.video_document_values)
        Media.objects.migrate_document_to_video_media(values)

        # We should have a new ``Video``...
        video = Video.objects.get()
        self.assertEqual(video.name, self.video_document.name)
        self.assertEqual(video.description, self.video_document.description)
        self.assertEqual(video.sort_order, self.video_document.sort_order)
        self.assertEqual(video.training, self.video_document.training)

        # ...and the associated ``VideoMedia``
        self.assertEqual(video.media.id, self.video_document.id)

        # We get our new file...
        self.assertTrue(
            os.path.isfile(
                self.get_file_absolute_path_for_field(video.media.file)
            )
        )

        # Our video file got correctly copied
        file_path = self.get_file_absolute_path_for_field(video.media.file)
        self.assertTrue(os.path.isfile(file_path))
        self.assertFalse(os.path.isfile(src_file_path))

    @override_settings(INSTANCE_IS_SLAVE=False)
    def test_migrate_invalid_document_to_video_media_on_master_should_work(self):
        """Test that the ``migrate_document_to_video_media`` method is working correctly"""

        # No ``Video`` objects yet
        self.assertEqual(Video.objects.count(), 0)

        # Video with invalid file
        invalid_video = DocumentFactory(trainee_document_file__filename='test.avi')

        # Migrate our ``Document``
        invalid_values = Media.objects.get_values_for_document(invalid_video)
        invalid_values = Media.objects.move_files_to_media(invalid_values)
        Media.objects.migrate_document_to_video_media(invalid_values)

        # We should not have a new ``Video``...
        self.assertEqual(Video.objects.count(), 0)

    @override_settings(INSTANCE_IS_SLAVE=True)
    def test_migrate_document_to_video_media_on_slave_should_work(self):
        """Test that the ``migrate_document_to_video_media`` method is working correctly"""

        # No ``Video`` objects yet
        self.assertEqual(Video.objects.count(), 0)

        # Original file exist
        src_file_path = self.get_file_absolute_path_for_field(
            self.video_document.trainee_document_file
        )
        self.assertTrue(os.path.isfile(src_file_path))

        # Migrate our ``Document``
        values = Media.objects.move_files_to_media(self.video_document_values)
        Media.objects.migrate_document_to_video_media(values)

        # Still no ``Video``objects since we are on a slave
        self.assertEqual(Video.objects.count(), 0)

        # Our video file got correctly copied
        file_path = os.path.join(
            settings.MEDIA_ROOT,
            values['trainee_document_file_name']
        )
        self.assertTrue(os.path.isfile(file_path))
        self.assertFalse(os.path.isfile(src_file_path))

    @override_settings(INSTANCE_IS_SLAVE=False)
    def test_migrate_document_to_presentation_on_master_should_work(self):
        """Test that the ``migrate_document_to_presentation``method is working correctly"""

        # No ``Presentation`` objects yet
        self.assertEqual(Presentation.objects.count(), 0)

        # Original files exists
        trainee_src_file_path = self.get_file_absolute_path_for_field(
            self.pdf_document.trainee_document_file
        )
        trainer_src_file_path = self.get_file_absolute_path_for_field(
            self.pdf_document.trainer_document_file
        )
        self.assertTrue(os.path.isfile(trainee_src_file_path))
        self.assertTrue(os.path.isfile(trainer_src_file_path))

        # Migrate our ``Document``
        values = Media.objects.move_files_to_media(self.pdf_document_values)
        Media.objects.migrate_document_to_presentation(values)

        # We should have a new ``Presentation`` (with the same id as the document)...
        presentation = Presentation.objects.get(id=self.pdf_document.id)
        self.assertEqual(presentation.name, self.pdf_document.name)
        self.assertEqual(presentation.description, self.pdf_document.description)
        self.assertEqual(presentation.sort_order, self.pdf_document.sort_order)
        self.assertEqual(presentation.training, self.pdf_document.training)
        self.assertEqual(presentation.is_accessible_by_trainees, not self.pdf_document.trainer_only)
        self.assertFalse(presentation.update_locally)

        # ...one non-restricted ``PresentationMedia`` got correctly copied ...
        trainee_presentation_media = PresentationMedia.objects.get(is_restricted=False)
        trainee_dest_file_path = self.get_file_absolute_path_for_field(
            trainee_presentation_media.media.file
        )
        self.assertTrue(os.path.isfile(trainee_dest_file_path))
        self.assertFalse(os.path.isfile(trainee_src_file_path))

        # ...and one restricted ``PresentationMedia``
        trainer_presentation_media = PresentationMedia.objects.get(is_restricted=True)
        trainer_dest_file_path = self.get_file_absolute_path_for_field(
            trainer_presentation_media.media.file
        )
        self.assertTrue(os.path.isfile(trainer_dest_file_path))
        self.assertFalse(os.path.isfile(trainer_src_file_path))

    @override_settings(INSTANCE_IS_SLAVE=False)
    def test_migrate_invalid_document_to_pdf_media_on_master_should_work(self):
        """Test that the ``migrate_document_to_video_media``
        method is working correctly with invalid files

        """

        # No ``Presentation`` objects yet
        self.assertEqual(Presentation.objects.count(), 0)

        # I - Document with invalid trainee file
        invalid_document = DocumentFactory(
            trainee_document_file__filename='test.pdf',
            trainer_document_file__filename='test.docx',
        )

        # Migrate our ``Document``
        invalid_values = Media.objects.get_values_for_document(invalid_document)
        invalid_values = Media.objects.move_files_to_media(invalid_values)
        Media.objects.migrate_document_to_presentation(invalid_values)

        # We should have a new ``Presentation``...
        self.assertTrue(Presentation.objects.filter(id=invalid_document.id).exists())

        # ...one non-restricted ``PresentationMedia``...
        trainee_presentation_media = PresentationMedia.objects.get(
            presentation_id=invalid_document.id,
            is_restricted=False
        )
        trainee_dest_file_path = self.get_file_absolute_path_for_field(
            trainee_presentation_media.media.file
        )
        self.assertTrue(os.path.isfile(trainee_dest_file_path))

        # ...but no restricted ``PresentationMedia`` ...
        self.assertFalse(PresentationMedia.objects.filter(
            presentation_id=invalid_document.id,
            is_restricted=True
        ).exists())

        # II - Document with invalid trainee file
        invalid_document = DocumentFactory(
            trainee_document_file__filename='test.docx',
            trainer_document_file__filename='test.pdf',
        )

        # Migrate our ``Document``
        invalid_values = Media.objects.get_values_for_document(invalid_document)
        invalid_values = Media.objects.move_files_to_media(invalid_values)
        Media.objects.migrate_document_to_presentation(invalid_values)

        # We should have a new ``Presentation``...
        self.assertTrue(Presentation.objects.filter(id=invalid_document.id).exists())

        # ...but no non-restricted ``PresentationMedia``...
        self.assertFalse(PresentationMedia.objects.filter(
            presentation_id=invalid_document.id,
            is_restricted=False
        ).exists())

        # ...and one restricted ``PresentationMedia`` ...
        self.assertTrue(PresentationMedia.objects.filter(
            presentation_id=invalid_document.id,
            is_restricted=True
        ).exists())

    @override_settings(INSTANCE_IS_SLAVE=True)
    def test_migrate_document_to_presentation_on_slave_should_work(self):
        """Test that the ``migrate_document_to_presentation``method is working correctly"""

        # No ``Presentation`` objects yet
        self.assertEqual(Presentation.objects.count(), 0)

        # Original files exists
        trainee_src_file_path = self.get_file_absolute_path_for_field(
            self.pdf_document.trainee_document_file
        )
        trainer_src_file_path = self.get_file_absolute_path_for_field(
            self.pdf_document.trainer_document_file
        )
        self.assertTrue(os.path.isfile(trainee_src_file_path))
        self.assertTrue(os.path.isfile(trainer_src_file_path))

        # Migrate our ``Document``
        values = Media.objects.move_files_to_media(self.pdf_document_values)
        Media.objects.migrate_document_to_presentation(values)

        # We should have a new ``Presentation`` (with the same id as the document)...
        presentation = Presentation.objects.get(id=self.pdf_document.id)
        self.assertEqual(presentation.name, self.pdf_document.name)
        self.assertEqual(presentation.description, self.pdf_document.description)
        self.assertEqual(presentation.sort_order, self.pdf_document.sort_order)
        self.assertEqual(presentation.training, self.pdf_document.training)
        self.assertEqual(presentation.is_accessible_by_trainees, not self.pdf_document.trainer_only)
        self.assertFalse(presentation.update_locally)

        # ...but we don't have any ``PresentationMedia`` (theses should be create only on MASTER)
        self.assertEqual(PresentationMedia.objects.count(), 0)

        # ...one non-restricted ``PresentationMedia`` got correctly copied ...
        trainee_dest_file_path = os.path.join(
            settings.MEDIA_ROOT,
            values['trainee_document_file_name']
        )
        self.assertTrue(os.path.isfile(trainee_dest_file_path))
        self.assertFalse(os.path.isfile(trainee_src_file_path))

        # ...and one restricted ``PresentationMedia``
        trainer_dest_file_path = os.path.join(
            settings.MEDIA_ROOT,
            values['trainer_document_file_name']
        )
        self.assertTrue(os.path.isfile(trainer_dest_file_path))
        self.assertFalse(os.path.isfile(trainer_src_file_path))

    @override_settings(INSTANCE_IS_SLAVE=False)
    def test_migrate_documents_to_media_should_delete_the_old_files(self):
        """Test that the ``migrate_documents_to_media`` method should delete the old files"""

        # We create a fake file
        file_paths = [
            self.get_file_absolute_path_for_field(
                self.video_document.trainee_document_file
            ),
            self.get_file_absolute_path_for_field(
                self.pdf_document.trainee_document_file
            ),
            self.get_file_absolute_path_for_field(
                self.pdf_document.trainer_document_file
            )
        ]

        # Our files exists
        for file_path in file_paths:
            with self.subTest(file_path=file_path):
                self.assertTrue(os.path.isfile(file_path))

        # Run the migration
        Media.objects.migrate_documents_to_media()

        # Our files don't exists anymore
        for file_path in file_paths:
            with self.subTest(file_path=file_path):
                self.assertFalse(os.path.isfile(file_path))

    def test_migrate_notes_for_document_should_work(self):
        """Test that the ``migrate_presentation_notes``method should migrate the notes"""

        # We don't have any ``Note``s yet
        self.assertEqual(Note.objects.count(), 0)

        # Firstly, we get our notes values
        note_values = Media.objects.get_note_values_for_document_id(self.pdf_document.id)

        # Then, we need to migrate our Document
        values = Media.objects.move_files_to_media(self.pdf_document_values)
        self.pdf_document.delete()
        Media.objects.migrate_document_to_presentation(values)

        # Now, we can migrate our ``Note``s
        Media.objects.migrate_document_notes(note_values)

        # We should have any "free note"...
        self.assertEqual(Note.objects.filter(presentation_id=None).count(), 0)

        # And one "presentation note"
        media_presentation_note = Note.objects.get(presentation_id=self.pdf_document.id)
        self.assertEqual(media_presentation_note.id, self.document_note.id)
        self.assertEqual(media_presentation_note.user, self.document_note.user)
        self.assertEqual(media_presentation_note.training_session, self.document_note.session)
        self.assertEqual(media_presentation_note.content, self.document_note.text)
        self.assertEqual(media_presentation_note.presentation_page, self.document_note.page)

    def test_migrate_multiple_notes_by_pages_should_work(self):
        """
        Test that the ``migrate_presentation_notes``method should migrate the notes
        when more than one by page

        """

        # create multiple notes for one page
        NoteFactory.create_batch(
            5,
            user=self.trainer,
            session=self.training_session,
            text='.',
            document=self.pdf_document,
            page=1
        )
        # create multiple notes for an other user
        NoteFactory.create_batch(
            5,
            session=self.training_session,
            text='.',
            document=self.pdf_document,
            page=1
        )

        # Firstly, we get our notes values
        notes_values = Media.objects.get_note_values_for_document_id(self.pdf_document.id)

        # Then, we need to migrate our Document
        values = Media.objects.move_files_to_media(self.pdf_document_values)
        self.pdf_document.delete()
        Media.objects.migrate_document_to_presentation(values)

        # Now, we can migrate our ``Note``s
        Media.objects.migrate_document_notes(notes_values)

        trainer_notes_values = [
            note_values
            for note_values in notes_values if note_values['user'] == self.trainer.id
        ]

        # get the first note for trainer
        reference_note = sorted(
            trainer_notes_values,
            key=itemgetter(
                'user',
                'session',
                'document_id',
                'page',
                'id'
            )
        )[0]

        # merged content, 5 notes containing '.'
        merged_content = '/n'.join(['.'] * 5)

        # Only one "presentation note" for the trainer
        media_presentation_note = Note.objects.get(
            presentation_id=self.pdf_document.id,
            user_id=self.trainer.id
        )

        self.assertEqual(media_presentation_note.id, reference_note['id'])
        self.assertEqual(media_presentation_note.user_id, reference_note['user'])
        self.assertEqual(media_presentation_note.training_session_id, reference_note['session'])
        self.assertEqual(media_presentation_note.content, merged_content)
        self.assertEqual(media_presentation_note.presentation_page, reference_note['page'])

    def test_migrate_free_notes_should_work(self):
        """Test that the ``migrate_free_notes``method should migrate the notes"""

        # We don't have any ``Note``s yet
        self.assertEqual(Note.objects.count(), 0)

        # Now, we can migrate our free ``Note``s
        Media.objects.migrate_free_notes()

        # We should have one "free note"...
        media_free_note = Note.objects.get(presentation_id=None)
        self.assertEqual(media_free_note.id, self.free_note.id)
        self.assertEqual(media_free_note.user, self.free_note.user)
        self.assertEqual(media_free_note.training_session, self.free_note.session)
        self.assertEqual(media_free_note.content, self.free_note.text)

        # And any "presentation note"
        self.assertEqual(Note.objects.exclude(presentation_id=None).count(), 0)

    @override_settings(INSTANCE_IS_SLAVE=True)
    def test_migrate_documents_to_media_on_slave_should_migrate_the_documents(self):
        """Test that the ``migrate_documents_to_media`` should work"""

        # At first, we should have 2 documents...
        self.assertEqual(Document.objects.count(), 2)

        # ...and 0 presentation / video
        self.assertEqual(Presentation.objects.count(), 0)
        self.assertEqual(Video.objects.count(), 0)

        # Run the migration
        Media.objects.migrate_documents_to_media()

        # Now, we should have 0 documents...
        self.assertEqual(Document.objects.count(), 0)

        # ...one presentation
        self.assertEqual(Presentation.objects.count(), 1)

        # ...0 video (We are on a slave instance)
        self.assertEqual(Video.objects.count(), 0)

    @override_settings(INSTANCE_IS_SLAVE=False)
    def test_migrate_documents_to_media_on_master_should_migrate_the_documents(self):
        """Test that the ``migrate_documents_to_media`` should work"""

        # At first, we should have 2 documents...
        self.assertEqual(Document.objects.count(), 2)

        # ...and 0 presentation / video
        self.assertEqual(Presentation.objects.count(), 0)
        self.assertEqual(Video.objects.count(), 0)

        # Run the migration
        Media.objects.migrate_documents_to_media()

        # Now, we should have 0 documents...
        self.assertEqual(Document.objects.count(), 0)

        # ...one presentation
        self.assertEqual(Presentation.objects.count(), 1)

        # ...and one video
        self.assertEqual(Video.objects.count(), 1)

    @override_settings(INSTANCE_IS_SLAVE=False)
    def test_migrate_documents_to_media_with_duplicated_document(self):
        """Test the ``migrate_documents_to_media`` method with a duplicated document"""

        # Duplicate our document
        duplicated_document = self.pdf_document.duplicate_in_training(self.pdf_document.training)

        # Create one note on this document
        NoteFactory(
            user=self.trainee,
            session=self.training_session,
            text='document note',
            document=duplicated_document,
            page=1
        )

        # Migrate our documents
        Media.objects.migrate_documents_to_media()

        # We should have two presentations
        self.assertEqual(Presentation.objects.count(), 2)

        # We should have one note on our first document
        self.assertEqual(
            Note.objects.filter(presentation_id=self.pdf_document.id).count(),
            1
        )

        # And one note on our duplicated document
        self.assertEqual(
            Note.objects.filter(presentation_id=duplicated_document.id).count(),
            1
        )
