import json

from datetime import datetime, timezone

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 NestedRuleListViewTestCase(TestCase):
    def setUp(self):
        self.user = UserFactory(password="test")
        self.client.force_login(self.user)

    def test_simple(self):
        rule = FeedFactory.create(user=self.user)
        FeedPostFactory.create_batch(size=5, rule=rule)

        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data["results"]), 5)
        self.assertEqual(data["next"], None)
        self.assertEqual(data["previous"], None)

    def test_pagination(self):
        rule = FeedFactory.create(user=self.user)

        posts = sorted(
            FeedPostFactory.create_batch(size=80, rule=rule),
            key=lambda post: post.publication_date,
            reverse=True,
        )

        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
            {"count": 30},
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertTrue(data["next"])
        self.assertFalse(data["previous"])

        self.assertEqual(len(data["results"]), 30)
        self.assertEqual(
            [post["id"] for post in data["results"]], [post.id for post in posts[:30]]
        )

    def test_empty(self):
        rule = FeedFactory.create(user=self.user)

        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data["results"]), 0)

    def test_not_known(self):
        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": 0})
        )

        self.assertEqual(response.status_code, 404)

    def test_post(self):
        rule = FeedFactory.create(user=self.user)

        response = self.client.post(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
            data=json.dumps({}),
            content_type="application/json",
        )
        data = response.json()

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

    def test_patch(self):
        rule = FeedFactory.create(user=self.user)

        response = self.client.patch(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
            data=json.dumps({}),
            content_type="application/json",
        )
        data = response.json()

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

    def test_put(self):
        rule = FeedFactory.create(user=self.user)

        response = self.client.put(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
            data=json.dumps({}),
            content_type="application/json",
        )
        data = response.json()

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

    def test_delete(self):
        rule = FeedFactory.create(user=self.user)

        response = self.client.delete(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
            data=json.dumps({}),
            content_type="application/json",
        )
        data = response.json()

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

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

        rule = FeedFactory(user=self.user)

        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
        )

        self.assertEqual(response.status_code, 403)

    def test_rule_with_unauthorized_user(self):
        other_user = UserFactory()
        rule = FeedFactory(user=other_user)

        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
        )

        self.assertEqual(response.status_code, 403)

    def test_posts_ordering(self):
        rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))

        posts = [
            FeedPostFactory(
                title="I'm the first post",
                rule=rule,
                publication_date=(
                    datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc)
                ),
            ),
            FeedPostFactory(
                title="I'm the second post",
                rule=rule,
                publication_date=(
                    datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc)
                ),
            ),
            FeedPostFactory(
                title="I'm the third post",
                rule=rule,
                publication_date=(
                    datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc)
                ),
            ),
        ]

        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data["results"]), 3)

        self.assertEqual(data["results"][0]["id"], posts[1].pk)
        self.assertEqual(data["results"][1]["id"], posts[2].pk)
        self.assertEqual(data["results"][2]["id"], posts[0].pk)

    def test_only_posts_from_rule_are_returned(self):
        rule = FeedFactory.create(user=self.user)
        other_rule = FeedFactory.create(user=self.user)

        FeedPostFactory.create_batch(size=5, rule=rule)
        FeedPostFactory.create_batch(size=5, rule=other_rule)

        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
        )
        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data["results"]), 5)

        for post in data["results"]:
            with self.subTest(post=post):
                self.assertEqual(post["rule"]["id"], rule.pk)

    def test_posts(self):
        rule = FeedFactory.create(user=self.user)

        FeedPostFactory.create_batch(size=10, rule=rule, read=False)
        FeedPostFactory.create_batch(size=10, rule=rule, read=True)

        response = self.client.get(
            reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
        )

        data = response.json()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data["results"]), 10)

        for post in data["results"]:
            with self.subTest(post=post):
                self.assertEqual(post["read"], False)
