import json

from django.test import TestCase
from django.urls import reverse

from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory


class PostDetailViewTestCase(TestCase):
    def setUp(self):
        self.user = UserFactory(password="test")
        self.client.force_login(self.user)

    def test_simple(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(rule=rule)

        response = self.client.get(
            reverse("api:news:core:posts-detail", args=[post.pk])
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(data["id"], post.pk)

        self.assertTrue("title" in data)
        self.assertTrue("body" in data)
        self.assertTrue("author" in data)
        self.assertTrue("publicationDate" in data)
        self.assertTrue("url" in data)
        self.assertTrue("rule" in data)
        self.assertTrue("remoteIdentifier" in data)

    def test_not_known(self):
        response = self.client.get(reverse("api:news:core:posts-detail", args=[100]))
        data = response.json()

        self.assertEqual(response.status_code, 404)
        self.assertEqual(data["detail"], "No Post matches the given query.")

    def test_post(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(rule=rule)

        response = self.client.post(
            reverse("api:news:core:posts-detail", args=[post.pk])
        )
        data = response.json()

        self.assertEqual(response.status_code, 405)
        self.assertEqual(data["detail"], 'Method "POST" not allowed.')

    def test_patch(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(title="This is clickbait for sure", rule=rule)

        response = self.client.patch(
            reverse("api:news:core:posts-detail", args=[post.pk]),
            data=json.dumps({"title": "This title is very accurate"}),
            content_type="application/json",
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(data["title"], "This title is very accurate")

    def test_identifier_cannot_be_changed(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(title="This is clickbait for sure", rule=rule)

        response = self.client.patch(
            reverse("api:news:core:posts-detail", args=[post.pk]),
            data=json.dumps({"id": 44}),
            content_type="application/json",
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(data["id"], post.pk)

    def test_rule_cannot_be_changed(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        new_rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(title="This is clickbait for sure", rule=rule)

        response = self.client.patch(
            reverse("api:news:core:posts-detail", args=[post.pk]),
            data=json.dumps(
                {
                    "rule": reverse(
                        "api:news:collection:rules-detail", args=[new_rule.pk]
                    )
                }
            ),
            content_type="application/json",
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)

        self.assertTrue(data["rule"], rule.pk)

    def test_put(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(title="This is clickbait for sure", rule=rule)

        response = self.client.put(
            reverse("api:news:core:posts-detail", args=[post.pk]),
            data=json.dumps({"title": "This title is very accurate"}),
            content_type="application/json",
        )

        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(data["title"], "This title is very accurate")

    def test_delete(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(rule=rule)

        response = self.client.delete(
            reverse("api:news:core:posts-detail", args=[post.pk])
        )
        data = response.json()

        self.assertEqual(response.status_code, 405)
        self.assertEqual(data["detail"], 'Method "DELETE" not allowed.')

    def test_post_with_unauthenticated_user_without_category(self):
        self.client.logout()

        rule = FeedFactory(user=self.user, category=None)
        post = FeedPostFactory(rule=rule)

        response = self.client.get(
            reverse("api:news:core:posts-detail", args=[post.pk])
        )

        self.assertEqual(response.status_code, 403)

    def test_post_with_unauthenticated_user_with_category(self):
        self.client.logout()

        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(rule=rule)

        response = self.client.get(
            reverse("api:news:core:posts-detail", args=[post.pk])
        )

        self.assertEqual(response.status_code, 403)

    def test_post_with_unauthorized_user_without_category(self):
        other_user = UserFactory()
        rule = FeedFactory(user=other_user, category=None)
        post = FeedPostFactory(rule=rule)

        response = self.client.get(
            reverse("api:news:core:posts-detail", args=[post.pk])
        )

        self.assertEqual(response.status_code, 403)

    def test_post_with_unauthorized_user_with_category(self):
        other_user = UserFactory()
        rule = FeedFactory(user=other_user, category=CategoryFactory(user=other_user))
        post = FeedPostFactory(rule=rule)

        response = self.client.get(
            reverse("api:news:core:posts-detail", args=[post.pk])
        )

        self.assertEqual(response.status_code, 403)

    def test_post_with_different_user_for_category_and_rule(self):
        other_user = UserFactory()
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=other_user))
        post = FeedPostFactory(rule=rule)

        response = self.client.get(
            reverse("api:news:core:posts-detail", args=[post.pk])
        )

        self.assertEqual(response.status_code, 403)

    def test_mark_read(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(rule=rule, read=False)

        response = self.client.patch(
            reverse("api:news:core:posts-detail", args=[post.pk]),
            data=json.dumps({"read": True}),
            content_type="application/json",
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(data["read"], True)

    def test_mark_unread(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(rule=rule, read=True)

        response = self.client.patch(
            reverse("api:news:core:posts-detail", args=[post.pk]),
            data=json.dumps({"read": False}),
            content_type="application/json",
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(data["read"], False)

    def test_mark_saved(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(rule=rule, saved=False)

        response = self.client.patch(
            reverse("api:news:core:posts-detail", args=[post.pk]),
            data=json.dumps({"saved": True}),
            content_type="application/json",
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(data["saved"], True)

    def test_mark_unsaved(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
        post = FeedPostFactory(rule=rule, saved=True)

        response = self.client.patch(
            reverse("api:news:core:posts-detail", args=[post.pk]),
            data=json.dumps({"saved": False}),
            content_type="application/json",
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(data["saved"], False)
