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

from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import CollectionRuleFactory
from newsreader.news.core.models import Category
from newsreader.news.core.tests.factories import CategoryFactory


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

    def test_simple(self):
        response = self.client.get(self.url)

        self.assertEquals(response.status_code, 200)


class CategoryCreateViewTestCase(CategoryViewTestCase, TestCase):
    def setUp(self):
        super().setUp()

        self.url = reverse("news:core:category-create")

    def test_creation(self):
        rules = CollectionRuleFactory.create_batch(size=4, user=self.user)

        data = {
            "name": "new-category",
            "rules": [rule.pk for rule in rules],
            "user": self.user.pk,
        }
        response = self.client.post(self.url, data)

        self.assertEquals(response.status_code, 302)

        category = Category.objects.get(name="new-category")

        self.assertCountEqual(category.rule_ids, [rule.pk for rule in rules])

    def test_collection_rules_only_from_user(self):
        other_user = UserFactory()
        other_rules = CollectionRuleFactory.create_batch(size=4, user=other_user)

        response = self.client.get(self.url)

        for rule in other_rules:
            self.assertNotContains(response, rule.name)

    def test_creation_with_other_user_rules(self):
        other_user = UserFactory()
        other_rules = CollectionRuleFactory.create_batch(
            size=4, user=other_user, category=None
        )

        CollectionRuleFactory.create_batch(size=3, user=self.user, category=None)

        data = {
            "name": "new-category",
            "rules": [rule.pk for rule in other_rules],
            "user": self.user.pk,
        }

        response = self.client.post(self.url, data)

        self.assertContains(response, "not one of the available choices")
        self.assertEquals(Category.objects.count(), 0)

    def test_unique_together(self):
        category = CategoryFactory(name="category", user=self.user)

        data = {"name": "category", "user": self.user.pk, "rules": []}
        response = self.client.post(self.url, data)

        categories = Category.objects.all()

        self.assertContains(response, "already exists")

        self.assertCountEqual(categories, [category])


class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
    def setUp(self):
        super().setUp()

        self.category = CategoryFactory(name="category", user=self.user)
        self.url = reverse("news:core:category-update", args=[self.category.pk])

    def test_name_change(self):
        data = {"name": "durp", "user": self.user.pk}
        self.client.post(self.url, data)

        self.category.refresh_from_db()

        self.assertEquals(self.category.name, "durp")

    def test_add_collection_rules(self):
        rules = CollectionRuleFactory.create_batch(size=4, user=self.user)
        data = {
            "name": self.category.name,
            "rules": [rule.pk for rule in rules],
            "user": self.user.pk,
        }

        self.client.post(self.url, data)

        self.category.refresh_from_db()

        # this actually checks for sequence contents too
        # see https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertCountEqual
        self.assertCountEqual(self.category.rule_ids, [rule.pk for rule in rules])

    def test_collection_rule_change(self):
        other_rules = CollectionRuleFactory.create_batch(size=4, user=self.user)
        other_category = CategoryFactory(user=self.user)
        other_category.rules.set([*other_rules])

        current_rules = CollectionRuleFactory.create_batch(size=3, user=self.user)
        self.category.rules.set([*current_rules])

        self.assertCountEqual(
            self.category.rule_ids, [rule.pk for rule in current_rules]
        )

        data = {
            "name": self.category.name,
            "rules": [rule.pk for rule in other_rules],
            "user": self.user.pk,
        }

        self.client.post(self.url, data)

        self.category.refresh_from_db()
        self.assertCountEqual(self.category.rule_ids, [rule.pk for rule in other_rules])

        other_category.refresh_from_db()
        self.assertCountEqual(other_category.rule_ids, [])

    def test_collection_rule_removal(self):
        current_rules = CollectionRuleFactory.create_batch(size=3, user=self.user)
        self.category.rules.set([*current_rules])

        self.assertCountEqual(
            self.category.rule_ids, [rule.pk for rule in current_rules]
        )

        data = {"name": "durp", "user": self.user.pk}
        self.client.post(self.url, data)

        self.category.refresh_from_db()

        self.assertEquals(self.category.rules.count(), 0)

    def test_collection_rules_only_from_user(self):
        other_user = UserFactory()
        other_rules = CollectionRuleFactory.create_batch(size=4, user=other_user)

        response = self.client.get(self.url)

        for rule in other_rules:
            self.assertNotContains(response, rule.name)

    def test_update_category_of_other_user(self):
        other_user = UserFactory()
        other_rules = CollectionRuleFactory.create_batch(size=4, user=other_user)

        other_category = CategoryFactory(name="other category", user=other_user)
        other_category.rules.set([*other_rules])

        data = {"name": "durp", "user": other_user.pk}
        other_url = reverse("news:core:category-update", args=[other_category.pk])
        response = self.client.post(other_url, data)

        self.assertEquals(response.status_code, 404)

        other_category.refresh_from_db()

        self.assertEquals(other_category.name, "other category")
        self.assertEquals(other_category.rules.count(), 4)

    def test_category_update_with_other_user_rules(self):
        other_user = UserFactory()
        other_rules = CollectionRuleFactory.create_batch(size=4, user=other_user)
        other_category = CategoryFactory(user=other_user)
        other_category.rules.set([*other_rules])

        current_rules = CollectionRuleFactory.create_batch(size=3, user=self.user)
        self.category.rules.set([*current_rules])

        self.assertCountEqual(
            self.category.rule_ids, [rule.pk for rule in current_rules]
        )

        data = {
            "name": self.category.name,
            "rules": [rule.pk for rule in other_rules],
            "user": self.user.pk,
        }

        response = self.client.post(self.url, data)

        self.assertContains(response, "not one of the available choices")

        self.category.refresh_from_db()
        self.assertCountEqual(
            self.category.rule_ids, [rule.pk for rule in current_rules]
        )

        other_category.refresh_from_db()
        self.assertCountEqual(
            other_category.rule_ids, [rule.pk for rule in other_rules]
        )

    def test_unique_together(self):
        other_category = CategoryFactory(name="other category", user=self.user)

        url = reverse("news:core:category-update", args=[other_category.pk])
        data = {"name": "category", "user": self.user.pk, "rules": []}
        response = self.client.post(url, data)

        categories = Category.objects.all()

        self.assertContains(response, "already exists")

        self.assertCountEqual(categories, [self.category, other_category])
