# -*- coding: utf-8 -*-

from __future__ import unicode_literals
import datetime

from django.test import TestCase
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.template import Template, Context
from django.conf import settings
from django.utils import timezone

from djconfig.utils import override_djconfig

from ...core.tests import utils
from ...category.models import Category
from .models import TopicPrivate
from .forms import TopicForPrivateForm, TopicPrivateInviteForm,\
    TopicPrivateManyForm, TopicPrivateJoinForm
from .tags import render_invite_form
from ..models import Topic
from ...comment.bookmark.models import CommentBookmark
from .. import utils as utils_topic
from ..notification.models import TopicNotification
from .utils import notify_access
from . import views as private_views


class TopicPrivateViewTest(TestCase):

    def setUp(self):
        cache.clear()
        self.user = utils.create_user()
        self.user2 = utils.create_user()

    def test_private_publish(self):
        """
        POST, create private topic
        """
        utils.login(self)
        form_data = {'comment': 'foo', 'title': 'foobar', 'users': self.user2.username}
        response = self.client.post(reverse('spirit:topic:private:publish'),
                                    form_data)
        private = TopicPrivate.objects.last()
        expected_url = private.get_absolute_url()
        self.assertRedirects(response, expected_url, status_code=302)

        response = self.client.get(reverse('spirit:topic:private:publish'))
        self.assertEqual(response.status_code, 200)

    def test_private_publish_create_notifications(self):
        """
        Should create notifications for invited members
        """
        utils.login(self)
        user = utils.create_user()
        form_data = {'comment': 'foo', 'title': 'foobar', 'users': [user.username, ]}
        response = self.client.post(reverse('spirit:topic:private:publish'),
                                    form_data)
        self.assertEqual(response.status_code, 302)

        topic_private = TopicPrivate.objects.last()
        self.assertEqual(len(TopicNotification.objects.filter(topic=topic_private.topic)), 2)
        self.assertEqual(len(TopicNotification.objects.filter(topic=topic_private.topic, user=user)), 1)
        self.assertEqual(len(TopicNotification.objects.filter(topic=topic_private.topic, user=self.user)), 1)

    def test_private_publish_user(self):
        """
        create private topic with user as initial value
        """
        utils.login(self)
        response = self.client.get(reverse('spirit:topic:private:publish', kwargs={'user_id': self.user2.pk, }))
        self.assertEqual(response.context['tpform'].initial['users'], [self.user2.username, ])

    def test_private_detail(self):
        """
        private topic detail
        """
        utils.login(self)
        private = utils.create_private_topic(user=self.user)

        comment1 = utils.create_comment(topic=private.topic)
        comment2 = utils.create_comment(topic=private.topic)

        category = utils.create_category()
        topic2 = utils.create_topic(category=category)
        utils.create_comment(topic=topic2)

        response = self.client.get(reverse('spirit:topic:private:detail', kwargs={'topic_id': private.topic.pk,
                                                                            'slug': private.topic.slug}))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context['topic'], private.topic)
        self.assertEqual(list(response.context['comments']), [comment1, comment2])

    @override_djconfig(comments_per_page=2)
    def test_private_detail_view_paginate(self):
        """
        should display topic with comments, page 1
        """
        utils.login(self)
        private = utils.create_private_topic(user=self.user)

        comment1 = utils.create_comment(topic=private.topic)
        comment2 = utils.create_comment(topic=private.topic)
        utils.create_comment(topic=private.topic)  # comment3

        response = self.client.get(reverse('spirit:topic:private:detail', kwargs={'topic_id': private.topic.pk,
                                                                            'slug': private.topic.slug}))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context['topic'], private.topic)
        self.assertEqual(list(response.context['comments']), [comment1, comment2])

    def test_topic_private_detail_viewed(self):
        """
        Calls utils.topic_viewed
        """
        def mocked_topic_viewed(request, topic):
            self._user = request.user
            self._topic = topic

        org_viewed, utils_topic.topic_viewed = utils_topic.topic_viewed, mocked_topic_viewed
        try:
            utils.login(self)
            category = utils.create_category()
            topic = utils.create_topic(category=category, user=self.user)
            response = self.client.get(reverse('spirit:topic:detail', kwargs={'pk': topic.pk, 'slug': topic.slug}))
            self.assertEqual(response.status_code, 200)
            self.assertEqual(self._topic, topic)
            self.assertEqual(self._user, self.user)
        finally:
            utils_topic.topic_viewed = org_viewed

    def test_private_access_create(self):
        """
        private topic access creation
        """
        utils.login(self)
        private = utils.create_private_topic(user=self.user)
        utils.create_comment(topic=private.topic)
        form_data = {'user': self.user2.username, }
        response = self.client.post(reverse('spirit:topic:private:access-create', kwargs={'topic_id': private.topic.pk, }),
                                    form_data)
        expected_url = private.get_absolute_url()
        self.assertRedirects(response, expected_url, status_code=302)
        self.assertEqual(len(TopicPrivate.objects.filter(user=self.user2, topic=private.topic)), 1)

    def test_private_access_create_invalid(self):
        """
        Only the topic owner should be able to invite
        """
        utils.login(self)
        private = utils.create_private_topic(user=self.user2)
        TopicPrivate.objects.create(user=self.user, topic=private.topic)
        user = utils.create_user()
        form_data = {'user': user.username, }
        response = self.client.post(reverse('spirit:topic:private:access-create', kwargs={'topic_id': private.topic.pk, }),
                                    form_data)
        self.assertEqual(response.status_code, 404)

    def test_private_access_create_notify_access(self):
        """
        notify_access
        """
        def mocked_notify_access(user, topic_private):
            self._user = user
            self._topic_private = topic_private

        org_access, private_views.notify_access = private_views.notify_access, mocked_notify_access
        try:
            utils.login(self)
            private = utils.create_private_topic(user=self.user)
            form_data = {'user': self.user2.username, }
            response = self.client.post(reverse('spirit:topic:private:access-create', kwargs={'topic_id': private.topic.pk, }),
                                        form_data)
            self.assertEqual(response.status_code, 302)
            self.assertEqual(self._topic_private, TopicPrivate.objects.last())
            self.assertEqual(self._user, self.user2)
        finally:
            private_views.notify_access = org_access

    def test_private_access_delete(self):
        """
        private topic access deletion
        """
        utils.login(self)
        private = utils.create_private_topic(user=self.user)
        private2 = TopicPrivate.objects.create(user=self.user2, topic=private.topic)
        form_data = {}
        response = self.client.post(reverse('spirit:topic:private:access-remove', kwargs={'pk': private2.pk, }),
                                    form_data)
        expected_url = private.get_absolute_url()
        self.assertRedirects(response, expected_url, status_code=302)

    def test_private_access_delete_invalid(self):
        """
        Only the topic owner should be able to remove accesses
        """
        utils.login(self)
        private = utils.create_private_topic(user=self.user2)
        TopicPrivate.objects.create(user=self.user, topic=private.topic)
        form_data = {}
        response = self.client.post(reverse('spirit:topic:private:access-remove', kwargs={'pk': private.pk, }),
                                    form_data)
        self.assertEqual(response.status_code, 404)

    def test_private_access_delete_leave(self):
        """
        user should be able to remove himself
        """
        utils.login(self)
        private = utils.create_private_topic(user=self.user2)
        private2_leave = TopicPrivate.objects.create(user=self.user, topic=private.topic)
        form_data = {}
        response = self.client.post(reverse('spirit:topic:private:access-remove', kwargs={'pk': private2_leave.pk, }),
                                    form_data)
        expected_url = reverse("spirit:topic:private:index")
        self.assertRedirects(response, expected_url, status_code=302)

    def test_private_list(self):
        """
        private topic list
        """
        private = utils.create_private_topic(user=self.user)
        # dont show private topics from other users
        TopicPrivate.objects.create(user=self.user2, topic=private.topic)
        # dont show topics from other categories
        category = utils.create_category()
        utils.create_topic(category, user=self.user)

        utils.login(self)
        response = self.client.get(reverse('spirit:topic:private:index'))
        self.assertEqual(list(response.context['topics']), [private.topic, ])

    def test_private_list_order_topics(self):
        """
        private topic list ordered by last active
        """
        private_a = utils.create_private_topic(user=self.user)
        private_b = utils.create_private_topic(user=self.user)
        private_c = utils.create_private_topic(user=self.user)

        Topic.objects.filter(pk=private_a.topic.pk).update(last_active=timezone.now() - datetime.timedelta(days=10))
        Topic.objects.filter(pk=private_c.topic.pk).update(last_active=timezone.now() - datetime.timedelta(days=5))

        utils.login(self)
        response = self.client.get(reverse('spirit:topic:private:index'))
        self.assertEqual(list(response.context['topics']), [private_b.topic, private_c.topic, private_a.topic])

    def test_private_list_bookmarks(self):
        """
        private topic list with bookmarks
        """
        private = utils.create_private_topic(user=self.user)
        bookmark = CommentBookmark.objects.create(topic=private.topic, user=self.user)

        utils.login(self)
        response = self.client.get(reverse('spirit:topic:private:index'))
        self.assertEqual(list(response.context['topics']), [private.topic, ])
        self.assertEqual(response.context['topics'][0].bookmark, bookmark)

    @override_djconfig(topics_per_page=1)
    def test_private_list(self):
        """
        private topic list paginated
        """
        utils.create_private_topic(user=self.user)
        private = utils.create_private_topic(user=self.user)

        utils.login(self)
        response = self.client.get(reverse('spirit:topic:private:index'))
        self.assertEqual(list(response.context['topics']), [private.topic, ])

    def test_private_join(self):
        """
        private topic join
        """
        private = utils.create_private_topic(user=self.user)
        utils.create_comment(topic=private.topic)
        private.delete()

        utils.login(self)
        form_data = {}
        response = self.client.post(reverse('spirit:topic:private:join', kwargs={'topic_id': private.topic.pk, }),
                                    form_data)
        expected_url = private.get_absolute_url()
        self.assertRedirects(response, expected_url, status_code=302)

        response = self.client.get(reverse('spirit:topic:private:join', kwargs={'topic_id': private.topic.pk, }))
        self.assertEqual(response.status_code, 200)

    def test_private_join_invalid_regular_topic(self):
        """
        Only topics from the category private can be rejoined
        """
        category = utils.create_category()
        topic = utils.create_topic(category, user=self.user)

        utils.login(self)
        form_data = {}
        response = self.client.post(reverse('spirit:topic:private:join', kwargs={'topic_id': topic.pk, }),
                                    form_data)
        self.assertEqual(response.status_code, 404)

    def test_private_join_invalid_not_owner(self):
        """
        Only topic creators/owners can rejoin
        """
        private = utils.create_private_topic(user=self.user2)

        utils.login(self)
        form_data = {}
        response = self.client.post(reverse('spirit:topic:private:join', kwargs={'topic_id': private.topic.pk, }),
                                    form_data)
        self.assertEqual(response.status_code, 404)

    def test_private_join_access_notify_access(self):
        """
        notify_access
        """
        def mocked_notify_access(user, topic_private):
            self._user = user
            self._topic_private = topic_private

        org_access, private_views.notify_access = private_views.notify_access, mocked_notify_access
        try:
            private = utils.create_private_topic(user=self.user)
            private.delete()

            utils.login(self)
            form_data = {}
            response = self.client.post(reverse('spirit:topic:private:join', kwargs={'topic_id': private.topic.pk, }),
                                        form_data)
            self.assertEqual(response.status_code, 302)
            self.assertEqual(self._topic_private, TopicPrivate.objects.last())
            self.assertEqual(self._user, self.user)
        finally:
            private_views.notify_access = org_access

    def test_private_created_list(self):
        """
        private topic created list, shows only the private topics the user is no longer participating
        """
        category = utils.create_category()
        utils.create_topic(category, user=self.user)
        # it's the owner, left the topic
        private = utils.create_private_topic(user=self.user)
        private.delete()
        # has access and is the owner
        utils.create_private_topic(user=self.user)
        # does not has access
        utils.create_private_topic(user=self.user2)
        # has access but it's not owner
        private4 = utils.create_private_topic(user=self.user2)
        TopicPrivate.objects.create(user=self.user, topic=private4.topic)

        utils.login(self)
        response = self.client.get(reverse('spirit:topic:private:index-author'))
        self.assertEqual(list(response.context['topics']), [private.topic, ])

    def test_private_created_list_order_topics(self):
        """
        private topic created list ordered by last active
        """
        private_a = utils.create_private_topic(user=self.user)
        private_b = utils.create_private_topic(user=self.user)
        private_c = utils.create_private_topic(user=self.user)
        private_a.delete()
        private_b.delete()
        private_c.delete()

        Topic.objects.filter(pk=private_a.topic.pk).update(last_active=timezone.now() - datetime.timedelta(days=10))
        Topic.objects.filter(pk=private_c.topic.pk).update(last_active=timezone.now() - datetime.timedelta(days=5))

        utils.login(self)
        response = self.client.get(reverse('spirit:topic:private:index-author'))
        self.assertEqual(list(response.context['topics']), [private_b.topic, private_c.topic, private_a.topic])

    @override_djconfig(topics_per_page=1)
    def test_private_created_list_paginate(self):
        """
        private topic created list paginated
        """
        private = utils.create_private_topic(user=self.user)
        private.delete()
        private2 = utils.create_private_topic(user=self.user)
        private2.delete()

        utils.login(self)
        response = self.client.get(reverse('spirit:topic:private:index-author'))
        self.assertEqual(list(response.context['topics']), [private2.topic, ])


class TopicPrivateFormTest(TestCase):

    def setUp(self):
        cache.clear()
        self.user = utils.create_user()
        self.user2 = utils.create_user()

    def test_private_publish(self):
        """
        create simple topic
        """
        form_data = {'title': 'foo', }
        form = TopicForPrivateForm(data=form_data)
        self.assertEqual(form.is_valid(), True)

    def test_private_create_many(self):
        """
        create many private topics accesses
        """
        users = '%s, %s' % (self.user.username, self.user2.username)
        form_data = {'users': users, }
        form = TopicPrivateManyForm(self.user, data=form_data)
        self.assertEqual(form.is_valid(), True)

        category = Category.objects.get(pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK)
        topic = utils.create_topic(category=category, user=self.user)
        form.topic = topic
        privates_saved = form.save_m2m()
        self.assertEqual(len(privates_saved), 2)
        privates = TopicPrivate.objects.all()
        self.assertEqual([p.user.pk for p in privates], [self.user2.pk, self.user.pk])

    def test_private_create(self):
        """
        create single private topic access
        """
        category = Category.objects.get(pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK)
        topic = utils.create_topic(category=category, user=self.user)
        form_data = {'user': self.user.username, }
        form = TopicPrivateInviteForm(data=form_data)
        form.topic = topic
        self.assertEqual(form.is_valid(), True)

    def test_private_join(self):
        """
        re-join private topic if user is the creator
        """
        private = utils.create_private_topic(user=self.user)
        private.delete()

        form_data = {}
        form = TopicPrivateJoinForm(user=self.user, topic=private.topic, data=form_data)
        self.assertTrue(form.is_valid())
        private_topic = form.save()
        self.assertEqual(private_topic.topic, private.topic)
        self.assertEqual(private_topic.user, private.user)

        # topic private exists
        private = utils.create_private_topic(user=self.user)
        form = TopicPrivateJoinForm(user=self.user, topic=private.topic, data=form_data)
        self.assertFalse(form.is_valid())


class TopicTemplateTagsTest(TestCase):

    def setUp(self):
        cache.clear()
        self.user = utils.create_user()
        self.category = Category.objects.get(pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK)
        self.topic = utils.create_topic(category=self.category, user=self.user)

    def test_render_invite_form(self):
        """
        display invite form
        """
        out = Template(
            "{% load spirit_tags %}"
            "{% render_invite_form topic %}"
        ).render(Context({'topic': self.topic, }))
        self.assertNotEqual(out, "")
        context = render_invite_form(self.topic)
        self.assertEqual(context['next'], None)
        self.assertIsInstance(context['form'], TopicPrivateInviteForm)
        self.assertEqual(context['topic'], self.topic)


class TopicPrivateUtilsTest(TestCase):

    def setUp(self):
        cache.clear()
        self.user = utils.create_user()
        self.user2 = utils.create_user()
        self.category = utils.create_category()
        self.topic = utils.create_topic(self.category)
        self.comment = utils.create_comment(topic=self.topic)

    def test_topic_private_notify_access(self):
        """
        Should create a notification
        """
        private = utils.create_private_topic()
        comment = utils.create_comment(topic=private.topic)
        notify_access(user=private.user, topic_private=private)
        notification = TopicNotification.objects.get(user=private.user, topic=private.topic)
        self.assertTrue(notification.is_active)
        self.assertFalse(notification.is_read)
        self.assertEqual(notification.comment, comment)