from unittest.mock import Mock, call
from datetime import timedelta

from superdesk.tests import TestCase
from superdesk.utc import utcnow
from superdesk.utc import query_datetime

from .remove_exported_files import RemoveExportedFiles


class MockMediaFS:
    """Simple Mocked Media FS"""

    def __init__(self, files):
        self.files = files
        self.delete = Mock()
        self.delete.side_effect = self._delete
        self.find = Mock()
        self.find.side_effect = self._find

    def _delete(self, _id):
        del self.files[_id]

    def _find(self, folder=None, upload_date=None):
        files = []
        for file in self.files.values():
            if (upload_date is not None and not query_datetime(file['upload_date'], upload_date)) \
                    or not file['filename'].startswith(folder):
                continue
            files.append(file)
        return files


class RemoveExportedFilesTest(TestCase):

    def setUp(self):
        # Create a backup of the original media reference so we can set it back up again later
        self.original_media = self.app.media
        self.now = utcnow()
        self.app.media = MockMediaFS({
            'a1': {'_id': 'a1', 'filename': 'aa', 'upload_date': self.now},
            'b2': {'_id': 'b2', 'filename': 'bb', 'upload_date': self.now - timedelta(hours=20)},
            'c3': {'_id': 'c3', 'filename': 'cc', 'upload_date': self.now - timedelta(hours=25)},
            'd4': {'_id': 'd4', 'filename': 'dd', 'upload_date': self.now - timedelta(hours=36)},
            'e5': {'_id': 'e5', 'filename': 'temp/ee', 'upload_date': self.now},
            'f6': {'_id': 'f6', 'filename': 'temp/ff', 'upload_date': self.now - timedelta(hours=20)},
            'g7': {'_id': 'g7', 'filename': 'temp/gg', 'upload_date': self.now - timedelta(hours=25)},
            'h8': {'_id': 'h8', 'filename': 'temp/hh', 'upload_date': self.now - timedelta(hours=36)}
        })
        self.can_expire = ['g7', 'h8']

        self.command = RemoveExportedFiles()

    def tearDown(self):
        """Ensure to restore the original media reference"""
        self.app.media = self.original_media

    def test_get_file_ids(self):
        expire_at = utcnow() - timedelta(hours=24)
        for file_id in self.command._get_file_ids(expire_at):
            self.assertIn(file_id, self.can_expire)

        kwargs = {
            'folder': 'temp',
            'upload_date': {'$lte': expire_at}
        }
        self.app.media.find.assert_called_once_with(**kwargs)

    def test_remove_exported_files(self):
        expire_at = utcnow() - timedelta(hours=24)
        self.command._remove_exported_files(expire_at)

        for file_id in self.app.media.files:
            self.assertNotIn(file_id, self.can_expire)

        self.app.media.delete.assert_has_calls([call('g7'), call('h8')], any_order=True)
        self.assertEqual(self.app.media.delete.call_count, 2)

    def test_run(self):
        self.command.run()
        for file_id in self.app.media.files:
            self.assertNotIn(file_id, self.can_expire)

        self.app.media.delete.assert_has_calls([call('g7'), call('h8')], any_order=True)
        self.assertEqual(self.app.media.delete.call_count, 2)
