# Copyright 2008-2015 Canonical
# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# For further info, check  http://launchpad.net/magicicada-server

"""Test the data gateways."""

from __future__ import unicode_literals

import logging
import posixpath
import os
import types
import uuid

from datetime import timedelta
from operator import attrgetter

from django.db import IntegrityError
from django.utils.timezone import now

from magicicada.filesync import errors, utils
from magicicada.filesync.dbmanager import fsync_commit
from magicicada.filesync.models import (
    STATUS_DEAD,
    STATUS_LIVE,
    ContentBlob,
    Download,
    MoveFromShare,
    Share,
    StorageObject,
    StorageUser,
    UploadJob,
)
from magicicada.filesync.notifier.notifier import (
    EventNotifier,
    ShareAccepted,
    ShareCreated,
    ShareDeclined,
    ShareDeleted,
    UDFCreate,
    UDFDelete,
    VolumeNewGeneration,
)
from magicicada.filesync.services import (
    DAODownload,
    DAOStorageUser,
    DAOUserVolume,
    DirectoryNode,
    FileNode,
    GatewayBase,
    ReadWriteVolumeGateway,
    SharedDirectory,
    StorageUserGateway,
    SystemGateway,
    fix_all_udfs_with_generation_out_of_sync,
    fix_udfs_with_generation_out_of_sync,
    make_storage_user,
    timing_metric,
)
from magicicada.testing.testcase import BaseTestCase


class FakeShare(object):
    """A share-like object."""

    def __init__(self, id, name, root_id, shared_by_id, shared_to_id,
                 access, accepted):
        """Initialize an instance."""
        self.id = id
        self.name = name
        self.root_id = root_id
        self.shared_by_id = shared_by_id
        self.shared_to_id = shared_to_id
        self.access = access
        self.accepted = accepted


class FakeUDF(object):
    """A udf-like object."""

    def __init__(self, owner_id, id, root_id, path):
        """Initialize an instance."""
        self.owner_id = owner_id
        self.id = id
        self.root_id = root_id
        self.path = path


class DummyNotifier(EventNotifier):
    """Keep track of notifications sent from gateway via notifier."""

    def __init__(self):
        super(DummyNotifier, self).__init__()
        self.notifications = []

    def on_event(self, value):
        self.notifications.append(value)

    def reset(self):
        self.notifications = []


class GatewayBaseTestCase(BaseTestCase):
    """Test the System level gateway."""

    def setUp(self):
        super(GatewayBaseTestCase, self).setUp()
        self.dummy_notifier = DummyNotifier()
        self.gw = GatewayBase(notifier=self.dummy_notifier)

    def test_get_user(self):
        """Test get_user"""
        user = self.factory.make_user()
        user2 = self.gw.get_user(user.id, session_id='QWERTY')
        self.assertEqual(user.id, user2.id)
        self.assertEqual(user.username, user2.username)
        self.assertEqual(user2._gateway.session_id, 'QWERTY')

    def test_get_user_username(self):
        """Test get_user with username"""
        user = self.factory.make_user()
        user2 = self.gw.get_user(username=user.username, session_id='QWERTY')
        self.assertEqual(user.id, user2.id)
        self.assertEqual(user.username, user2.username)
        self.assertEqual(user2._gateway.session_id, 'QWERTY')

    def test_get_user_locked(self):
        """Test get_user with a locked user."""
        user = self.factory.make_user(locked=True)
        self.assertRaises(
            errors.LockedUserError,
            self.gw.get_user, user.id, session_id='QWERTY')

    def test_get_user_locked_ignore_lock(self):
        """Test get_user with a locked user, but ignore the lock."""
        user = self.factory.make_user(locked=True)
        nuser = self.gw.get_user(
            user.id, session_id='QWERTY', ignore_lock=True)
        self.assertEqual(user.id, nuser.id)

    def test_split_in_list(self):
        """Test the utils.split_in_list function."""
        biglist = [i for i in range(1000)]
        # test with various max sizes
        for i in range(3, 210):
            sublists = utils.split_in_list(biglist, max=i)
            items = 0
            for s in sublists:
                items += len(s)
                self.assertTrue(len(s) <= i)
            # make sure we have the same number of items
            self.assertEqual(items, 1000)

    def test_queue_new_generation(self):
        """When a new volume generation."""
        vol_id = uuid.uuid4()
        with fsync_commit():
            self.gw.queue_new_generation(1, vol_id, 3)
        self.assertEqual(self.dummy_notifier.notifications, [
            VolumeNewGeneration(1, vol_id, 3, self.gw.session_id)
        ])

    def _te
st_queue_share_event(self, event_class, method_name):
        """Test share events (generic form)."""
        share_id = uuid.uuid4()
  
      name = 'foobar'
        root_id = uuid.uuid4()
        shared_by_id = 10
        shared_to_id = 11
        access = 'Read'
        accepted = 'False'
        share = FakeShare(share_id, name, root_id, shared_by_id, shared_to_id,
                          access, accepted)
        with fsync_commit():
            getattr(self.gw, method_name)(share)
        self.assertEqual(self.dummy_notifier.notifications, [
            event_class(share_id, name, root_id, shared_by_id, shared_to_id,
                        access, accepted)
        ])

    def test_queue_share_created(self):
        """Test event generated when a share is created."""
        self._test_queue_share_event(ShareCreated, 'queue_share_created')

    def test_queue_share_deleted(self):
        """Test event generated when a share is deleted."""
        self._test_queue_share_event(ShareDeleted, 'queue_share_deleted')

    def test_queue_share_accepted(self):
        """Test event generated when a share is accepted."""
        self._test_queue_share_event(ShareAccepted, 'queue_share_accepted')

    def test_queue_share_declined(self):
        """Test event generated when a share is declined."""
        self._test_queue_share_event(ShareDeclined, 'queue_share_declined')

    def test_queue_udf_create(self):
        """Test event generated when a UDF has been created."""
        owner_id = 10
        udf_id = uuid.uuid4()
        root_id = uuid.uuid4()
        suggested_path = 'foo/bar/baz'
        udf = FakeUDF(owner_id, root_id, udf_id, suggested_path)
        with fsync_commit():
            self.gw.queue_udf_create(udf)
        self.assertEqual(self.dummy_notifier.notifications, [
            UDFCreate(owner_id, root_id, udf_id, suggested_path,
                      self.gw.session_id)
        ])

    def test_queue_udf_delete(self):
        """Test event generated when a UDF is deleted."""
        owner_id = 10
        udf_id = uuid.uuid4()
        root_id = uuid.uuid4()
        suggested_path = 'foo/bar/baz'
        udf = FakeUDF(owner_id, udf_id, root_id, suggested_path)
        with fsync_commit():
            self.gw.queue_udf_delete(udf)
        self.assertEqual(
            self.dummy_notifier.notifications, [UDFDelete(owner_id, udf_id)])


class EventNotificationTest(BaseTestCase):
    """Tests to make sure the events trigger notifications."""

    def setUp(self):
        super(EventNotificationTest, self).setUp()
        self.user = make_storage_user(username='user1')
        self.dummy_notifier = DummyNotifier()
        self.gw = GatewayBase(
            session_id=uuid.uuid4(), notifier=self.dummy_notifier)
        sel