from datetime import datetime, timedelta

from django.contrib.auth.models import User, Group, AnonymousUser
from django.test import TestCase
from django.utils import timezone
from mock import patch, PropertyMock

from astrobin.models import Image
from astrobin.tests.generators import Generators
from astrobin_apps_users.services import UserService
from nested_comments.tests.nested_comments_generators import NestedCommentsGenerators
from toggleproperties.models import ToggleProperty


class TestUserService(TestCase):
    def test_get_case_insensitive_none(self):
        with self.assertRaises(User.DoesNotExist):
            UserService.get_case_insensitive("foo")

    def test_get_case_insensitive_one(self):
        user = Generators.user(username="one")

        self.assertEqual(user.username, UserService.get_case_insensitive("one").username)
        self.assertEqual(user.username, UserService.get_case_insensitive("onE").username)

    def test_get_case_insensitive_two(self):
        user1 = Generators.user(username="one")
        user2 = Generators.user(username="oNe")

        self.assertEqual(user1.username, UserService.get_case_insensitive(user1.username).username)
        self.assertEqual(user2.username, UserService.get_case_insensitive(user2.username).username)

        with self.assertRaises(User.DoesNotExist):
            UserService.get_case_insensitive("One")

    def test_get_all_images(self):
        user = Generators.user()
        image = Generators.image(user=user)
        wip = Generators.image(user=user, is_wip=True)
        deleted = Generators.image(user=user)

        deleted.delete()

        self.assertTrue(image in UserService(user).get_all_images())
        self.assertTrue(wip in UserService(user).get_all_images())
        self.assertFalse(deleted in UserService(user).get_all_images())

    def test_get_public_images(self):
        user = Generators.user()
        image = Generators.image(user=user)
        wip = Generators.image(user=user, is_wip=True)

        self.assertTrue(image in UserService(user).get_public_images())
        self.assertFalse(wip in UserService(user).get_public_images())

    def test_get_public_images_has_collaborations(self):
        user = Generators.user()
        image = Generators.image()

        image.collaborators.add(user)

        self.assertTrue(image in UserService(user).get_public_images())

    def test_get_wip_images(self):
        user = Generators.user()
        image = Generators.image(user=user)
        wip = Generators.image(user=user, is_wip=True)

        self.assertFalse(image in UserService(user).get_wip_images())
        self.assertTrue(wip in UserService(user).get_wip_images())

    def test_get_deleted_images(self):
        user = Generators.user()
        image = Generators.image(user=user)
        deleted = Generators.image(user=user)

        deleted.delete()

        self.assertFalse(image in UserService(user).get_deleted_images())
        self.assertTrue(deleted in UserService(user).get_deleted_images())

    def test_bookmarked_images(self):
        user1 = Generators.user()
        user2 = Generators.user()

        image = Generators.image(user=user1)

        ToggleProperty.objects.create_toggleproperty("bookmark", image, user2)

        bookmarked = UserService(user2).get_bookmarked_images()

        self.assertTrue(image in bookmarked)

    def test_liked_images(self):
        user1 = Generators.user()
        user2 = Generators.user()

        image = Generators.image(user=user1)

        ToggleProperty.objects.create_toggleproperty("like", image, user2)

        liked = UserService(user2).get_liked_images()

        self.assertTrue(image in liked)

    def test_get_image_numbers(self):
        user1 = Generators.user()
        user2 = Generators.user()

        Generators.image(user=user1)
        Generators.image(user=user1, is_wip=True)

        image2 = Generators.image(user=user2)
        ToggleProperty.objects.create_toggleproperty("bookmark", image2, user1)
        ToggleProperty.objects.create_toggleproperty("like", image2, user1)

        image_numbers = UserService(user1).get_image_numbers()

        self.assertEqual(image_numbers['public_images_no'], 1)
        self.assertEqual(image_numbers['wip_images_no'], 1)

    def test_can_like_image_superuser(self):
        user = Generators.user()
        image = Generators.image()
        user.is_superuser = True
        user.save()

        self.assertTrue(UserService(user).can_like(image))

    @patch('django.contrib.auth.models.User.is_authenticated', new_callable=PropertyMock)
    def test_can_like_image_anon(self, is_authenticated):
        user = Generators.user()
        image = Generators.image()

        is_authenticated.return_value = False

        self.assertFalse(UserService(user).can_like(image))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_image_index_zero(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = True

        user = Generators.user()
        image = Generators.image()

        self.assertTrue(UserService(user).can_like(image))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_image_same_user(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = False

        user = Generators.user()
        image = Generators.image(user=user)

        self.assertFalse(UserService(user).can_like(image))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_image_ok(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = False

        user = Generators.user()
        image = Generators.image()

        self.assertTrue(UserService(user).can_like(image))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_comment_same_user(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = False

        user = Generators.user()
        comment = NestedCommentsGenerators.comment(author=user)

        self.assertFalse(UserService(user).can_like(comment))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_comment_ok(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = False

        user = Generators.user()
        comment = NestedCommentsGenerators.comment()

        self.assertTrue(UserService(user).can_like(comment))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_comment_same_user(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = False

        user = Generators.user()
        comment = NestedCommentsGenerators.comment(author=user)

        self.assertFalse(UserService(user).can_like(comment))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_post_ok(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = False

        user = Generators.user()
        post = Generators.forum_post()

        self.assertTrue(UserService(user).can_like(post))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_post_same_user(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = False

        user = Generators.user()
        post = Generators.forum_post(user=user)

        self.assertFalse(UserService(user).can_like(post))

    @patch('astrobin_apps_premium.templatetags.astrobin_apps_premium_tags.is_free')
    @patch('astrobin.models.UserProfile.get_scores')
    def test_can_like_post_closed_topic(self, get_scores, is_free):
        get_scores.return_value = {'user_scores_index': 0}
        is_free.return_value = False

        user = Generators.user()
        post = Generators.forum_post()

        post.topic.closed = True
        post.topic.save()

        self.assertFalse(UserService(user).can_like(post))

    @patch('django.contrib.auth.models.User.is_authenticated', new_callable=PropertyMock)
    def test_can_unlike_anon(self, is_authenticated):
        image = Generators.image()
        like = Generators.like(image)

        is_authenticated.return_value = False

        self.assertFalse(UserService(like.user).can_unlike(image))
        self.assertEqual("ANONYMOUS", UserService(like.user).can_unlike_reason(image))

    @patch('django.contrib.auth.models.User.is_authenticated', new_callable=PropertyMock)
    def test_can_unlike_never_liked(self, is_authenticated):
        is_authenticated.return_value = True

        image = Generators.image()
        user = Generators.user()

        self.assertFalse(UserService(user).can_unlike(image))
        self.assertEqual("NEVER_LIKED", UserService(user).can_unlike_reason(image))

    @patch('django.contrib.auth.models.User.is_authenticated', new_callable=PropertyMock)
    def test_can_unlike_too_late(self, is_authenticated):
        is_authenticated.return_value = True

        image = Generators.image()
        like = Generators.like(image)

        like.created_on = timezone.now() - timedelta(hours=2)
        like.save()

        self.assertFalse(UserService(like.user).can_unlike(image))
        self.assertEqual("TOO_LATE", UserService(like.user).can_unlike_reason(image))

    @patch('django.contrib.auth.models.User.is_authenticated', new_callable=PropertyMock)
    def test_can_unlike(self, is_authenticated):
        is_authenticated.return_value = True

        image = Generators.image()
        like = Generators.like(image)

        like.created_on = timezone.now() - timedelta(minutes=59)
        like.save()

        self.assertTrue(UserService(like.user).can_unlike(image))

    def test_get_users_in_group_sample_no_users(self):
        group = Group.objects.create(name='test_group')

        self.assertEqual(0, len(UserService.get_users_in_group_sample(group.name, 10)))

    def test_get_users_in_group_sample_one_user(self):
        group = Group.objects.create(name='test_group')
        user = Generators.user()
        user.groups.add(group)

        self.assertEqual(1, len(UserService.get_users_in_group_sample(group.name, 10)))
        self.assertEqual(1, len(UserService.get_users_in_group_sample(group.name, 50)))
        self.assertEqual(1, len(UserService.get_users_in_group_sample(group.name, 100)))

    def test_get_users_in_group_sample_with_exclude(self):
        group = Group.objects.create(name='test_group')
        user = Generators.user()
        user.groups.add(group)

        self.assertEqual(0, len(UserService.get_users_in_group_sample(group.name, 10,  user)))

    def test_get_users_in_group_sample_many_users(self):
        group = Group.objects.create(name='test_group')
        for i in range(100):
            user = Generators.user()
            user.groups.add(group)

        self.assertEqual(10, len(UserService.get_users_in_group_sample(group.name, 10)))
        self.assertEqual(50, len(UserService.get_users_in_group_sample(group.name, 50)))
        self.assertEqual(100, len(UserService.get_users_in_group_sample(group.name, 100)))

    def test_get_users_in_group_sample_odd_users(self):
        group = Group.objects.create(name='test_group')
        for i in range(9):
            user = Generators.user()
            user.groups.add(group)

        self.assertEqual(1, len(UserService.get_users_in_group_sample(group.name, 10)))
        self.assertEqual(5, len(UserService.get_users_in_group_sample(group.name, 50)))
        self.assertEqual(9, len(UserService.get_users_in_group_sample(group.name, 100)))

    def test_shadow_bans(self):
        a = Generators.user()
        b = Generators.user()

        self.assertFalse(UserService(a).shadow_bans(b))
        self.assertFalse(UserService(b).shadow_bans(a))

        a.userprofile.shadow_bans.add(b.userprofile)

        self.assertTrue(UserService(a).shadow_bans(b))
        self.assertFalse(UserService(b).shadow_bans(a))

    def test_shadow_bans_anonymous(self):
        a = AnonymousUser()
        b = Generators.user()

        self.assertFalse(UserService(a).shadow_bans(b))
        self.assertFalse(UserService(b).shadow_bans(a))

    def test_empty_trash(self):
        user = Generators.user()
        user2 = Generators.user()

        image = Generators.image(user=user)
        Generators.image(user=user2)

        UserService(user).empty_trash()

        self.assertEqual(1, Image.objects.filter(user=user).count())
        self.assertEqual(1, Image.objects.filter(user=user2).count())

        image.delete()

        self.assertEqual(0, Image.objects.filter(user=user).count())
        self.assertEqual(1, Image.objects.filter(user=user2).count())
        self.assertEqual(1, Image.deleted_objects.filter(user=user).count())

        UserService(user).empty_trash()

        self.assertEqual(0, Image.deleted_objects.filter(user=user).count())
        self.assertEqual(1, Image.objects.filter(user=user2).count())
