from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

from userena.contrib.umessages.forms import ComposeForm
from userena.contrib.umessages.models import Message, MessageRecipient

User = get_user_model()


class MessagesViewsTests(TestCase):
    fixtures = ["users", "messages"]

    def _test_login(self, named_url, **kwargs):
        """ Test that the view requires login """
        response = self.client.get(reverse(named_url, **kwargs))
        self.assertEqual(response.status_code, 302)

    def test_compose(self):
        """ A ``GET`` to the compose view """
        # Login is required.
        self._test_login("userena_umessages_compose")

        # Sign in
        client = self.client.login(username="john", password="blowfish")
        response = self.client.get(reverse("userena_umessages_compose"))

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "umessages/message_form.html")

        self.assertTrue(isinstance(response.context["form"], ComposeForm))

    def test_compose_post(self):
        """ ``POST`` to the compose view """
        client = self.client.login(username="john", password="blowfish")

        valid_data = {"to": "john", "body": "Hi"}

        # Check for a normal redirect
        response = self.client.post(
            reverse("userena_umessages_compose"), data=valid_data
        )

        self.assertRedirects(
            response, reverse("userena_umessages_detail", kwargs={"username": "john"})
        )

        # Check for a requested redirect
        valid_data["next"] = reverse("userena_umessages_compose")
        response = self.client.post(
            reverse("userena_umessages_compose"), data=valid_data
        )
        self.assertRedirects(response, valid_data["next"])

    def test_compose_recipients(self):
        """ A ``GET`` to the compose view with recipients """
        client = self.client.login(username="john", password="blowfish")

        valid_recipients = "john+jane"
        invalid_recipients = "johny+jane"

        # Test valid recipients
        response = self.client.get(
            reverse(
                "userena_umessages_compose_to", kwargs={"recipients": valid_recipients}
            )
        )

        self.assertEqual(response.status_code, 200)

        # Test the users
        jane = User.objects.get(username="jane")
        john = User.objects.get(username="john")
        self.assertEqual(response.context["recipients"][0], jane)
        self.assertEqual(response.context["recipients"][1], john)

        # Test that the initial data of the form is set.
        self.assertEqual(response.context["form"].initial["to"], [jane, john])

    def test_message_detail(self):
        """ A ``GET`` to the detail view """
        self._test_login("userena_umessages_detail", kwargs={"message_id": 2})

        # Sign in
        client = self.client.login(username="jane", password="blowfish")
        response = self.client.get(
            reverse("userena_umessages_detail", kwargs={"message_id": 1})
        )

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "umessages/message_detail.html")

        # Test that the message is read.
        jane = User.objects.get(pk=2)
        mr = MessageRecipient.objects.get(message=Message.objects.get(pk=1), user=jane)
        self.assertTrue(mr.read_at)

    def test_valid_message_remove(self):
        """ ``POST`` to remove a message """
        # Test that sign in is required
        response = self.client.post(reverse("userena_umessages_remove"))
        self.assertEqual(response.status_code, 302)

        # Sign in
        client = self.client.login(username="john", password="blowfish")

        # Test that only posts are allowed
        response = self.client.get(reverse("userena_umessages_remove"))
        self.assertEqual(response.status_code, 405)

        # Test a valid post to delete a senders message
        response = self.client.post(
            reverse("userena_umessages_remove"), data={"message_pks": "1"}
        )
        self.assertRedirects(response, reverse("userena_umessages_list"))
        msg = Message.objects.get(pk=1)
        self.assertTrue(msg.sender_deleted_at)

        # Test a valid post to delete a recipients message and a redirect
        client = self.client.login(username="jane", password="blowfish")
        response = self.client.post(
            reverse("userena_umessages_remove"),
            data={"message_pks": "1", "next": reverse("userena_umessages_list")},
        )
        self.assertRedirects(response, reverse("userena_umessages_list"))
        jane = User.objects.get(username="jane")
        mr = msg.messagerecipient_set.get(user=jane, message=msg)
        self.assertTrue(mr.deleted_at)

    def test_invalid_message_remove(self):
        """ ``POST`` to remove an invalid message """
        # Sign in
        client = self.client.login(username="john", password="blowfish")

        bef_len = Message.objects.filter(sender_deleted_at__isnull=False).count()
        response = self.client.post(
            reverse("userena_umessages_remove"), data={"message_pks": ["a", "b"]}
        )

        # The program should play nice, nothing happened.
        af_len = Message.objects.filter(sender_deleted_at__isnull=False).count()
        self.assertRedirects(response, reverse("userena_umessages_list"))
        self.assertEqual(bef_len, af_len)

    def test_valid_message_remove_multiple(self):
        """ ``POST`` to remove multiple messages """
        # Sign in
        client = self.client.login(username="john", password="blowfish")
        response = self.client.post(
            reverse("userena_umessages_remove"), data={"message_pks": [1, 2]}
        )
        self.assertRedirects(response, reverse("userena_umessages_list"))

        # Message #1 and #2 should be deleted
        msg_list = Message.objects.filter(
            pk__in=["1", "2"], sender_deleted_at__isnull=False
        )
        self.assertEqual(msg_list.count(), 2)

    def test_message_unremove(self):
        """ Unremove a message """
        client = self.client.login(username="john", password="blowfish")

        # Delete a message as owner
        response = self.client.post(
            reverse("userena_umessages_unremove"), data={"message_pks": [1]}
        )

        self.assertRedirects(response, reverse("userena_umessages_list"))

        # Delete the message as a recipient
        response = self.client.post(
            reverse("userena_umessages_unremove"), data={"message_pks": [2]}
        )

        self.assertRedirects(response, reverse("userena_umessages_list"))

    def test_message_list(self):
        """ ``GET`` the message list for a user """
        self._test_login("userena_umessages_list")

        client = self.client.login(username="john", password="blowfish")
        response = self.client.get(reverse("userena_umessages_list"))
        self.assertEqual(response.status_code, 200)

        self.assertTemplateUsed(response, "umessages/message_list.html")

    def test_message_detail(self):
        """ ``GET`` to a detail page between two users """
        self._test_login("userena_umessages_detail", kwargs={"username": "jane"})
        client = self.client.login(username="john", password="blowfish")

        response = self.client.get(
            reverse("userena_umessages_detail", kwargs={"username": "jane"})
        )

        self.assertEqual(response.status_code, 200)

        self.assertTemplateUsed(response, "umessages/message_detail.html")

        # Check that all the messages are marked as read.
        john = User.objects.get(pk=1)
        jane = User.objects.get(pk=2)
        unread_messages = MessageRecipient.objects.filter(
            user=john, message__sender=jane, read_at__isnull=True
        )

        self.assertEqual(len(unread_messages), 0)
