from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.utils import timezone

from misago.acl.testutils import override_acl
from misago.forums.models import Forum
from misago.users.testutils import AuthenticatedUserTestCase

from misago.threads import testutils
from misago.threads.models import Thread, ThreadParticipant


class ThreadParticipantsTests(AuthenticatedUserTestCase):
    ajax_header = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}

    def setUp(self):
        super(ThreadParticipantsTests, self).setUp()

        self.forum = Forum.objects.private_threads()
        self.thread = testutils.post_thread(self.forum)

    def test_participants_list(self):
        """participants list displays thread participants"""
        User = get_user_model()
        users = (
            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
            User.objects.create_user("Dam", "dam@bob.com", "pass123")
        )

        ThreadParticipant.objects.set_owner(self.thread, self.user)
        ThreadParticipant.objects.add_participant(self.thread, users[0])
        ThreadParticipant.objects.add_participant(self.thread, users[1])

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_participants', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug
        })

        response = self.client.get(link, **self.ajax_header)
        self.assertEqual(response.status_code, 200)

        owner_pos = response.content.find(self.user.get_absolute_url())
        for user in users:
            participant_pos = response.content.find(user.get_absolute_url())
            self.assertTrue(owner_pos < participant_pos)

    def test_edit_participants(self):
        """edit participants view displays thread participants"""
        User = get_user_model()
        users = (
            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
            User.objects.create_user("Dam", "dam@bob.com", "pass123")
        )

        ThreadParticipant.objects.set_owner(self.thread, self.user)
        ThreadParticipant.objects.add_participant(self.thread, users[0])
        ThreadParticipant.objects.add_participant(self.thread, users[1])

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_edit_participants', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug
        })

        response = self.client.get(link, **self.ajax_header)
        self.assertEqual(response.status_code, 200)

        owner_pos = response.content.find(self.user.get_absolute_url())
        for user in users:
            participant_pos = response.content.find(user.get_absolute_url())
            self.assertTrue(owner_pos < participant_pos)

    def test_owner_remove_participant(self):
        """remove participant allows owner to remove participant"""
        User = get_user_model()
        other_user = User.objects.create_user("Bob", "bob@bob.com", "pass123")

        ThreadParticipant.objects.set_owner(self.thread, self.user)
        ThreadParticipant.objects.add_participant(self.thread, other_user)

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_remove_participant', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug,
            'user_id': other_user.id,
        })

        response = self.client.post(link, **self.ajax_header)
        self.assertEqual(response.status_code, 200)

        self.assertEqual(self.thread.threadparticipant_set.count(), 1)
        owner = self.thread.threadparticipant_set.get(is_owner=True)
        self.assertEqual(owner.user, self.user)

        Thread.objects.get(pk=self.thread.pk)
        self.thread.threadparticipant_set.get(user=self.user)

    def test_owner_remove_non_participant(self):
        """remove participant handles attempt to remove invalid participant"""
        User = get_user_model()
        other_user = User.objects.create_user("Bob", "bob@bob.com", "pass123")

        ThreadParticipant.objects.set_owner(self.thread, self.user)
        ThreadParticipant.objects.add_participant(self.thread, other_user)

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_remove_participant', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug,
            'user_id': 123456,
        })

        response = self.client.post(link, **self.ajax_header)
        self.assertEqual(response.status_code, 200)

        self.assertEqual(self.thread.threadparticipant_set.count(), 2)
        owner = self.thread.threadparticipant_set.get(is_owner=True)
        self.assertEqual(owner.user, self.user)

        Thread.objects.get(pk=self.thread.pk)
        self.thread.threadparticipant_set.get(user=self.user)

    def test_non_owner_remove_participant(self):
        """non-owner cant remove participant"""
        User = get_user_model()
        other_user = User.objects.create_user("Bob", "bob@bob.com", "pass123")

        ThreadParticipant.objects.set_owner(self.thread, other_user)
        ThreadParticipant.objects.add_participant(self.thread, self.user)

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_remove_participant', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug,
            'user_id': other_user.pk,
        })

        response = self.client.post(link, **self.ajax_header)
        self.assertEqual(response.status_code, 406)

        self.assertEqual(self.thread.threadparticipant_set.count(), 2)
        owner = self.thread.threadparticipant_set.get(is_owner=True)
        self.assertEqual(owner.user, other_user)

        Thread.objects.get(pk=self.thread.pk)
        self.thread.threadparticipant_set.get(user=self.user)

    def test_owner_add_participant(self):
        """owner can add participants"""
        User = get_user_model()
        users = (
            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
            User.objects.create_user("Dam", "dam@bob.com", "pass123")
        )

        ThreadParticipant.objects.set_owner(self.thread, self.user)
        ThreadParticipant.objects.add_participant(self.thread, users[0])

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_add_participants', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug,
        })

        response = self.client.post(link, data={
            'users': 'Bob, Dam'
        }, **self.ajax_header)
        self.assertEqual(response.status_code, 200)

        self.assertEqual(self.thread.threadparticipant_set.count(), 3)
        for participant in self.thread.threadparticipant_set.all():
            if participant.is_owner:
                self.assertEqual(participant.user, self.user)
            else:
                self.assertIn(participant.user, users)

        Thread.objects.get(pk=self.thread.pk)
        self.thread.threadparticipant_set.get(user=self.user)

    def test_non_owner_add_participant(self):
        """non-owner cant add participants"""
        User = get_user_model()
        users = (
            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
            User.objects.create_user("Dam", "dam@bob.com", "pass123")
        )

        ThreadParticipant.objects.set_owner(self.thread, users[0])
        ThreadParticipant.objects.add_participant(self.thread, self.user)

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_add_participants', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug,
        })

        response = self.client.post(link, data={
            'users': 'Bob, Dam'
        }, **self.ajax_header)
        self.assertEqual(response.status_code, 406)

    def test_owner_leave_thread_new_owner(self):
        """
        leave thread view makes owner leave thread and makes new user owner
        """
        User = get_user_model()
        users = (
            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
            User.objects.create_user("Dam", "dam@bob.com", "pass123")
        )

        ThreadParticipant.objects.set_owner(self.thread, self.user)
        ThreadParticipant.objects.add_participant(self.thread, users[0])
        ThreadParticipant.objects.add_participant(self.thread, users[1])

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_leave', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug
        })

        response = self.client.post(link, **self.ajax_header)
        self.assertEqual(response.status_code, 302)

        self.assertEqual(self.thread.threadparticipant_set.count(), 2)
        new_owner = self.thread.threadparticipant_set.get(is_owner=True)
        self.assertNotEqual(new_owner.user, self.user)
        self.assertIn(new_owner.user, users)

        Thread.objects.get(pk=self.thread.pk)
        with self.assertRaises(ThreadParticipant.DoesNotExist):
            self.thread.threadparticipant_set.get(user=self.user)

    def test_owner_leave_thread_delete(self):
        """
        leave thread view makes owner leave thread and deletes abadoned thread
        """
        ThreadParticipant.objects.set_owner(self.thread, self.user)

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_leave', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug
        })

        response = self.client.post(link, **self.ajax_header)
        self.assertEqual(response.status_code, 302)

        with self.assertRaises(Thread.DoesNotExist):
            Thread.objects.get(pk=self.thread.pk)

        with self.assertRaises(ThreadParticipant.DoesNotExist):
            self.thread.threadparticipant_set.get(user=self.user)

    def test_participant_leave_thread(self):
        """
        leave thread view makes user leave thread
        """
        User = get_user_model()
        users = (
            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
            User.objects.create_user("Dam", "dam@bob.com", "pass123")
        )

        ThreadParticipant.objects.set_owner(self.thread, users[0])
        ThreadParticipant.objects.add_participant(self.thread, users[1])
        ThreadParticipant.objects.add_participant(self.thread, self.user)

        override_acl(self.user, {
            'can_use_private_threads': True,
            'can_moderate_private_threads': True
        })

        link = reverse('misago:private_thread_leave', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug
        })

        response = self.client.post(link, **self.ajax_header)
        self.assertEqual(response.status_code, 302)

        self.assertEqual(self.thread.threadparticipant_set.count(), 2)
        owner = self.thread.threadparticipant_set.get(is_owner=True)
        self.assertEqual(owner.user, users[0])

        for participants in self.thread.threadparticipant_set.all():
            self.assertIn(participants.user, users)

        Thread.objects.get(pk=self.thread.pk)
        with self.assertRaises(ThreadParticipant.DoesNotExist):
            self.thread.threadparticipant_set.get(user=self.user)