""" test for app action functionality """
import json
from unittest.mock import patch

from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory

from bookwyrm import models, views
from bookwyrm.tests.validate_html import validate_html


@patch("bookwyrm.activitystreams.add_user_statuses_task.delay")
@patch("bookwyrm.lists_stream.add_user_lists_task.delay")
class FollowViews(TestCase):
    """follows"""

    @classmethod
    def setUpTestData(cls):
        """we need basic test data and mocks"""
        models.SiteSettings.objects.create()
        with (
            patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"),
            patch("bookwyrm.activitystreams.populate_stream_task.delay"),
            patch("bookwyrm.lists_stream.populate_lists_task.delay"),
        ):
            cls.local_user = models.User.objects.create_user(
                "mouse@local.com",
                "mouse@mouse.com",
                "mouseword",
                local=True,
                localname="mouse",
                remote_id="https://example.com/users/mouse",
            )
        with patch("bookwyrm.models.user.set_remote_server"):
            cls.remote_user = models.User.objects.create_user(
                "rat",
                "rat@email.com",
                "ratword",
                local=False,
                remote_id="https://example.com/users/rat",
                inbox="https://example.com/users/rat/inbox",
                outbox="https://example.com/users/rat/outbox",
            )
        cls.group = Group.objects.create(name="editor")
        cls.group.permissions.add(
            Permission.objects.create(
                name="edit_book",
                codename="edit_book",
                content_type=ContentType.objects.get_for_model(models.User),
            ).id
        )
        cls.work = models.Work.objects.create(title="Test Work")
        cls.book = models.Edition.objects.create(
            title="Example Edition",
            remote_id="https://example.com/book/1",
            parent_work=cls.work,
        )

    def setUp(self):
        """individual test setup"""
        self.factory = RequestFactory()

    def test_handle_follow_remote(self, *_):
        """send a follow request"""
        request = self.factory.post("", {"user": self.remote_user.username})
        request.user = self.local_user
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)

        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            views.follow(request)

        rel = models.UserFollowRequest.objects.get()

        self.assertEqual(rel.user_subject, self.local_user)
        self.assertEqual(rel.user_object, self.remote_user)
        self.assertEqual(rel.status, "follow_request")

    def test_handle_follow_local_manually_approves(self, *_):
        """send a follow request"""
        with (
            patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"),
            patch("bookwyrm.activitystreams.populate_stream_task.delay"),
            patch("bookwyrm.lists_stream.populate_lists_task.delay"),
        ):
            rat = models.User.objects.create_user(
                "rat@local.com",
                "rat@rat.com",
                "ratword",
                local=True,
                localname="rat",
                remote_id="https://example.com/users/rat",
                manually_approves_followers=True,
            )
        request = self.factory.post("", {"user": rat})
        request.user = self.local_user
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)

        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            views.follow(request)
        rel = models.UserFollowRequest.objects.get()

        self.assertEqual(rel.user_subject, self.local_user)
        self.assertEqual(rel.user_object, rat)
        self.assertEqual(rel.status, "follow_request")

    def test_handle_follow_local(self, *_):
        """send a follow request"""
        with (
            patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"),
            patch("bookwyrm.activitystreams.populate_stream_task.delay"),
            patch("bookwyrm.lists_stream.populate_lists_task.delay"),
        ):
            rat = models.User.objects.create_user(
                "rat@local.com",
                "rat@rat.com",
                "ratword",
                local=True,
                localname="rat",
                remote_id="https://example.com/users/rat",
            )
        request = self.factory.post("", {"user": rat})
        request.user = self.local_user
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)

        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            views.follow(request)

        rel = models.UserFollows.objects.get()

        self.assertEqual(rel.user_subject, self.local_user)
        self.assertEqual(rel.user_object, rat)
        self.assertEqual(rel.status, "follows")

    @patch("bookwyrm.activitystreams.remove_user_statuses_task.delay")
    @patch("bookwyrm.lists_stream.remove_user_lists_task.delay")
    def test_handle_unfollow(self, *_):
        """send an unfollow"""
        request = self.factory.post("", {"user": self.remote_user.username})
        request.user = self.local_user
        self.remote_user.followers.add(self.local_user)
        self.assertEqual(self.remote_user.followers.count(), 1)
        with patch(
            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
        ) as mock:
            views.unfollow(request)
            self.assertEqual(mock.call_count, 1)
            activity = json.loads(mock.call_args_list[0][1]["args"][1])
            self.assertEqual(activity["type"], "Undo")

        self.assertEqual(self.remote_user.followers.count(), 0)

    def test_handle_accept(self, *_):
        """accept a follow request"""
        self.local_user.manually_approves_followers = True
        self.local_user.save(
            broadcast=False, update_fields=["manually_approves_followers"]
        )
        request = self.factory.post("", {"user": self.remote_user.username})
        request.user = self.local_user
        rel = models.UserFollowRequest.objects.create(
            user_subject=self.remote_user, user_object=self.local_user
        )

        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            views.accept_follow_request(request)
        # request should be deleted
        self.assertEqual(models.UserFollowRequest.objects.filter(id=rel.id).count(), 0)
        # follow relationship should exist
        self.assertEqual(self.local_user.followers.first(), self.remote_user)

    def test_handle_reject(self, *_):
        """reject a follow request"""
        self.local_user.manually_approves_followers = True
        self.local_user.save(
            broadcast=False, update_fields=["manually_approves_followers"]
        )
        request = self.factory.post("", {"user": self.remote_user.username})
        request.user = self.local_user
        rel = models.UserFollowRequest.objects.create(
            user_subject=self.remote_user, user_object=self.local_user
        )

        with patch(
            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
        ) as broadcast_mock:
            views.delete_follow_request(request)
            # did we send the reject activity?
            activity = json.loads(broadcast_mock.call_args[1]["args"][1])
            self.assertEqual(activity["actor"], self.local_user.remote_id)
            self.assertEqual(activity["object"]["object"], rel.user_object.remote_id)
            self.assertEqual(activity["type"], "Reject")
        # request should be deleted
        self.assertEqual(models.UserFollowRequest.objects.filter(id=rel.id).count(), 0)
        # follow relationship should not exist
        self.assertEqual(models.UserFollows.objects.filter(id=rel.id).count(), 0)

    def test_handle_reject_existing(self, *_):
        """reject a follow previously approved"""
        request = self.factory.post("", {"user": self.remote_user.username})
        request.user = self.local_user
        rel = models.UserFollows.objects.create(
            user_subject=self.remote_user, user_object=self.local_user
        )
        with patch(
            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
        ) as broadcast_mock:
            views.remove_follow(request, self.remote_user.id)
            # did we send the reject activity?
            activity = json.loads(broadcast_mock.call_args[1]["args"][1])
            self.assertEqual(activity["actor"], self.local_user.remote_id)
            self.assertEqual(activity["object"]["object"], rel.user_object.remote_id)
            self.assertEqual(activity["type"], "Reject")
        # follow relationship should not exist
        self.assertEqual(models.UserFollows.objects.filter(id=rel.id).count(), 0)

    def test_ostatus_follow_request(self, *_):
        """check ostatus subscribe template loads"""
        request = self.factory.get(
            "", {"acct": "https%3A%2F%2Fexample.com%2Fusers%2Frat"}
        )
        request.user = self.local_user
        result = views.ostatus_follow_request(request)
        self.assertIsInstance(result, TemplateResponse)
        validate_html(result.render())
        self.assertEqual(result.status_code, 200)

    def test_remote_follow_page(self, *_):
        """check remote follow page loads"""
        request = self.factory.get("", {"acct": "mouse@local.com"})
        request.user = self.remote_user
        result = views.remote_follow_page(request)
        self.assertIsInstance(result, TemplateResponse)
        validate_html(result.render())
        self.assertEqual(result.status_code, 200)

    def test_ostatus_follow_success(self, *_):
        """check remote follow success page loads"""
        request = self.factory.get("")
        request.user = self.remote_user
        request.following = "mouse@local.com"
        result = views.ostatus_follow_success(request)
        self.assertIsInstance(result, TemplateResponse)
        validate_html(result.render())
        self.assertEqual(result.status_code, 200)

    def test_remote_follow(self, *_):
        """check follow from remote page loads"""
        request = self.factory.post("", {"user": self.remote_user.id})
        request.user = self.remote_user
        request.remote_user = "mouse@local.com"
        result = views.remote_follow(request)
        self.assertIsInstance(result, TemplateResponse)
        validate_html(result.render())
        self.assertEqual(result.status_code, 200)
