import datetime

from django.test import TestCase
from django.utils import timezone, formats
from django.core.exceptions import ValidationError

from scarlet.versioning import manager

from . import models


class ModelTests(TestCase):
    fixtures = ("test_data.json",)

    def setUp(self):
        manager.activate("draft")

    def tearDown(self):
        manager.deactivate()

    def _check_different_book_versions(self, bd, bp):
        self.assertEqual(bd.object_id, bp.object_id)
        self.assertEqual(bd.id, bp.id)
        self.assertEqual(bd.pk, bp.pk)
        self.assertNotEqual(bd.vid, bp.vid)

    def _prepare_books(self):
        book = models.Book.objects.get(vid=1)
        book.publish()

        with manager.SwitchSchema("published"):
            bp = models.Book.normal.get(object_id=book.object_id)
        with manager.SwitchSchema("draft"):
            bd = models.Book.normal.get(object_id=book.object_id)

        return bp, bd

    def testNew(self):
        author = models.Author(name="MoYan")
        author.save()
        a = models.Author.objects.get(name="MoYan")
        self.assertTrue(a.pk)
        self.assertEqual(a.state, models.Author.DRAFT)

    def testCloneNoRelated(self):
        """
        class Cloneable _clone method.
        a clone has identical properties except vid
        """
        self.assertFalse(hasattr(models.BookNoRelated, "_clone_related"))

        book = models.BookNoRelated.objects.get(pk=1)
        last_save = book.last_save

        with manager.SwitchSchema("public"):
            n_books = models.BookNoRelated.objects.all().count()

        book._clone()
        book = models.BookNoRelated.objects.get(vid=1)

        with manager.SwitchSchema("public"):
            n_books_new = models.BookNoRelated.objects.all().count()

        self.assertEqual(n_books, n_books_new - 1)

        self.assertEqual(book.pk, 1)
        self.assertEqual(last_save, book.last_save)

        clone = models.BookNoRelated.objects.get(vid=2)
        self.assertEqual(clone.author, book.author)
        self.assertEqual(clone.object_id, book.object_id)

    def testCloneRelated(self):
        """
        attributes registered in _clone_related should be cloned as well
        """
        self.assertTrue(hasattr(models.Book, "_clone_related"))

        book = models.Book.objects.get(vid=1)
        n_review = models.Review.objects.all().count()
        self.assertEqual(n_review, 2)
        n_gallery = models.Gallery.objects.all().count()
        self.assertEqual(n_gallery, 2)

        book._clone()
        clone = models.Book.objects.get(vid__gt=1)
        book = models.Book.objects.get(vid=1)

        n_review = clone.review_set.all().count()
        self.assertEqual(n_review, 2)

        n_review = models.Review.objects.all().count()
        self.assertEqual(n_review, 4)

        n_gallery = models.Gallery.objects.all().count()
        self.assertEqual(n_gallery, 4)

    def testDeleteRelated(self):
        """
        related objects should be deleted as well
        """
        self.assertEqual(models.Book.objects.all().count(), 1)
        book = models.Book.objects.get(vid=1)

        self.assertEqual(models.Gallery.objects.all().count(), 2)
        self.assertEqual(models.Review.objects.all().count(), 2)

        book.delete()
        self.assertEqual(models.Book.objects.all().count(), 0)

        self.assertEqual(models.Gallery.objects.all().count(), 0)
        self.assertEqual(models.Review.objects.all().count(), 0)

    def testDeleteReverseM2M(self):
        no = models.NoReverse(name="test")
        no.save()
        with self.assertRaises(AssertionError):
            no.delete()

    def testDeleteVersion(self):
        """
        delete one version of an object should also delete all other versions
        """
        bp, bd = self._prepare_books()

        bp.delete()
        with manager.SwitchSchema("public"):
            self.assertFalse(models.Book.normal.filter(object_id=bd.object_id).exists())

        self.assertEqual(models.Gallery.objects.all().count(), 0)

    def testReverseM2MDeleteVersion(self):
        cartoon = models.Cartoon.objects.get(vid=1)
        cartoon.publish()

        with manager.SwitchSchema("public"):
            self.assertEqual(models.Cartoon.normal.all().count(), 2)

        klass = cartoon.get_version_class()
        klass.normal.filter(vid__gt=1).delete()

        self.assertEqual(models.Cartoon.normal.all().count(), 1)
        image = models.Image(pk=1)
        self.assertEqual(image.cartoons.all().count(), 1)

    def testDeleteWithM2M(self):
        cartoon = models.Cartoon.objects.get(vid=1)
        cartoon.publish()

        with manager.SwitchSchema("public"):
            self.assertEqual(models.Cartoon.normal.all().count(), 2)

            image = models.Image(pk=1)
            self.assertEqual(image.cartoons.all().count(), 1)

            cartoon.delete()
            self.assertEqual(image.cartoons.all().count(), 0)

    def testChangeRelated(self):
        """
        change related items of an object version won't affect other versions
        """
        bp, bd = self._prepare_books()

        with manager.SwitchSchema("draft"):
            bd_reviews = list(bd.review_set.all())
            bd_gallery = list(bd.galleries.all())

        # change related object
        bd_reviews[0].text = "changed"
        bd_reviews[0].save()
        bd_gallery[0].name = "changed"
        bd_gallery[0].save()

        self.assertTrue(bd.review_set.filter(text="changed").exists())
        self.assertTrue(bd.galleries.filter(name="changed").exists())

        with manager.SwitchSchema("published"):
            self.assertEqual(bp.review_set.all().count(), 2)
            self.assertEqual(bp.galleries.all().count(), 2)
            self.assertFalse(bp.review_set.filter(text="changed").exists())
            self.assertFalse(bp.galleries.filter(name="changed").exists())

    def testAddRelated(self):
        """
        add related item to an object version won't affect other versions
        """
        bp, bd = self._prepare_books()
        # add relationship
        g = models.Gallery(name="newly added")
        g.save()
        bp.galleries.add(g)

        with manager.SwitchSchema("published"):
            self.assertTrue(bp.galleries.filter(name="newly added").exists())
            self.assertEqual(bp.galleries.all().count(), 3)

        with manager.SwitchSchema("draft"):
            self.assertFalse(bd.galleries.filter(name="newly added").exists())
            self.assertEqual(bd.galleries.all().count(), 2)

        self.assertEqual(models.Gallery.objects.all().count(), 5)

    def testRelatedName(self):
        author = models.Author.objects.get(vid=1)
        self.assertTrue(hasattr(author, "works_version"))

    def testPublishAuthor(self):
        """
        publish is actually, make a clone of draft, and change the state
        of the clone to PUBLISHED
        """
        author = models.Author.objects.get(vid=1)
        self.assertEqual(author.state, models.Author.DRAFT)
        old_vid = author.vid

        author.publish()
        self.assertEqual(author.state, models.Author.PUBLISHED)
        self.assertNotEqual(author.vid, old_vid)

        author = models.Author.objects.get(vid=old_vid)
        self.assertEqual(author.state, models.Author.DRAFT)

    def testRegisterRelated(self):
        original = models.Book._clone_related
        setattr(models.Book, "_clone_related", [])
        models.Book.register_related("test")
        related = models.Book._clone_related
        self.assertEqual(related, ["test"])

        models.Book._clone_related = ("test",)
        models.Book.register_related("test2")
        related = models.Book._clone_related
        self.assertEqual(related, ["test", "test2"])

        setattr(models.Book, "_clone_related", original)

    def testGetVersion(self):
        author = models.Author.objects.get(pk=1)
        self.assertEqual(author.get_version(state=models.Author.PUBLISHED), None)

    def testPublishBook(self):
        from django.contrib.auth.models import User

        u = User.objects.create(username="admin")

        book = models.Book.objects.get(vid=1)
        book.publish(user=u)
        bd = models.Book.objects.get(object_id=book.object_id, state=models.Book.DRAFT)
        bp = book

        self.assertEqual(bp.user_published, "admin")

        self._check_different_book_versions(bd, bp)

    def testUnPublish(self):
        book = models.Book.objects.get(vid=1)
        klass = book.get_version_class()
        book.publish()
        self.assertEqual(klass.normal.filter().count(), 2)
        self.assertTrue(book.is_published)
        self.assertEqual(book.state, models.Book.PUBLISHED)

        book = models.Book.objects.get(vid=1)
        book.unpublish()
        self.assertEqual(klass.normal.filter().count(), 2)
        self.assertFalse(book.is_published)

    def testUnPublishWithScheduled(self):
        book = models.Book.objects.get(vid=1)
        klass = book.get_version_class()

        # schedule to publish
        t = timezone.now() + datetime.timedelta(days=7)
        book.publish(when=t)
        self.assertEqual(klass.normal.all().count(), 2)

        book = models.Book.objects.get(vid=1)
        book.unpublish()
        # make sure scheduled items get deleted
        self.assertFalse(
            klass.normal.filter(
                object_id=book.object_id, state=models.Book.SCHEDULED
            ).exists()
        )
        self.assertEqual(klass.normal.all().count(), 1)

    def testPublishWithPublishedAndScheduled(self):
        book = models.Book.objects.get(vid=1)
        klass = book.get_version_class()

        # publish the draft
        book.publish()
        self.assertEqual(klass.normal.all().count(), 2)
        self.assertTrue(klass.normal.filter(state=models.Book.PUBLISHED).exists())

        # schedule the draft to be published in future
        book = models.Book.objects.get(vid=1)
        t = timezone.now() + datetime.timedelta(days=20)
        book.publish(when=t)
        self.assertEqual(klass.normal.all().count(), 3)
        self.assertTrue(klass.normal.filter(state=models.Book.SCHEDULED).exists())

        # try publish the draft again; it will archive the item published above
        # and publish the current one
        book = models.Book.objects.get(vid=1)
        book.publish(when=timezone.now())
        self.assertEqual(klass.normal.all().count(), 4)
        self.assertTrue(klass.normal.filter(state=models.Book.ARCHIVED).exists())

    def testPublishWithScheduledOnly(self):
        book = models.Book.objects.get(vid=1)
        klass = book.get_version_class()

        # schedule the draft to be published in future
        book = models.Book.objects.get(vid=1)
        t = timezone.now() + datetime.timedelta(days=7)
        book.publish(when=t)
        self.assertEqual(klass.normal.all().count(), 2)
        self.assertTrue(klass.normal.filter(state=models.Book.SCHEDULED).exists())

        # the time stamp info in draft and scheduled items are identical
        draft = models.Book.objects.get(vid=1)
        self.assertEqual(draft.last_scheduled, book.last_scheduled)
        self.assertEqual(draft.date_published, t)
        self.assertEqual(book.date_published, t)

        # try publish the draft again; it should overwrite
        # scheduled item above and publish it now
        book = models.Book.objects.get(vid=1)
        book.publish()
        self.assertEqual(klass.normal.all().count(), 2)
        self.assertTrue(klass.normal.filter(state=models.Book.PUBLISHED).exists())

    def testMakeDraft(self):
        book = models.Book.objects.get(vid=1)
        self.assertEqual(book.name, "Book1")

        # draft cannot make draft -- return nothing inside make_draft function.
        with self.assertRaises(AssertionError):
            book.make_draft()

        klass = book.get_version_class()
        book = klass.normal.get(vid=1)
        book.make_draft()
        self.assertEqual(
            models.Book.objects.filter(object_id=book.object_id).count(), 1
        )

        book = klass.normal.get(vid=1)
        book.publish()

        # prepare a draft for deleting when calling make_draft
        draft = klass.normal.get(object_id=book.object_id, state=models.Book.DRAFT)
        self.assertEqual(draft.last_scheduled, draft.last_save)
        draft.name = "book2"
        draft.save()
        self.assertTrue(klass.normal.filter(name="book2").exists())

        # need to get the object again to get updated last_save value, coz the
        # one cached by django is not updated.
        draft = klass.normal.get(object_id=book.object_id, state=models.Book.DRAFT)
        self.assertEqual(draft.last_scheduled, book.last_scheduled)
        self.assertTrue(draft.last_scheduled < draft.last_save)

        book = klass.normal.get(object_id=book.object_id, state=models.Book.PUBLISHED)
        book.make_draft()
        # make sure the old draft get deleted
        self.assertFalse(klass.normal.filter(name="book2").exists())

        # make sure the underlying two versions of book are "same"
        manager.deactivate()
        bd = models.Book.objects.get(object_id=book.object_id, state=models.Book.DRAFT)
        bp = models.Book.objects.get(
            object_id=book.object_id, state=models.Book.PUBLISHED
        )
        manager.activate("draft")

        self.assertEqual(bd.name, "Book1")
        self._check_different_book_versions(bd, bp)

    def testRevert(self):
        """
        update the draft; revert to the archived;
        the update should be abolished.
        """
        obp, obd = self._prepare_books()
        obd.publish()
        klass = obp.get_version_class()

        with manager.SwitchSchema("public"):
            bp = klass.normal.get(object_id=obp.object_id, state=models.Book.PUBLISHED)
            bd = klass.normal.get(object_id=obp.object_id, state=models.Book.DRAFT)
            ba = klass.normal.get(object_id=obp.object_id, state=models.Book.ARCHIVED)

        bd.name = "the sea"
        bd.save()
        self.assertEqual(bd.name, "the sea")
        self.assertEqual(bp.name, "Book1")
        self.assertEqual(ba.name, "Book1")

        ba.make_draft()
        with manager.SwitchSchema("public"):
            self.assertEqual(models.Book.normal.all().count(), 3)
            bp2 = models.Book.normal.get(
                object_id=obp.object_id, state=models.Book.PUBLISHED
            )
            bd2 = models.Book.normal.get(
                object_id=obp.object_id, state=models.Book.DRAFT
            )
            ba2 = models.Book.normal.get(
                object_id=obp.object_id, state=models.Book.ARCHIVED
            )

        # old draft gets deleted,
        self.assertFalse(models.Book.objects.filter(vid=1).exists())
        # new draft generated
        self.assertEqual(bd2.name, "Book1")
        # other versions stay same
        self.assertEqual(bp2.name, "Book1")
        self.assertEqual(ba2.name, "Book1")

    def testDraftRelated(self):
        """
        get draft, and published. add new gallery item to draft, and publish
        it then revert back.

        remove gallery item from draft; publish it; revert back;
        """
        obp, obd = self._prepare_books()
        klass = obp.get_version_class()

        bp = klass.normal.get(object_id=obp.object_id, state=models.Book.PUBLISHED)
        bd = klass.normal.get(object_id=obp.object_id, state=models.Book.DRAFT)

        self.assertEqual(models.Gallery.objects.all().count(), 4)
        self.assertEqual(bd.vid, 1)

        # change draft
        g = models.Gallery(name="old man")
        g.save()
        bd.galleries.add(g)
        self.assertEqual(models.Gallery.objects.all().count(), 5)

        # change should occur only in draft
        self.assertEqual(bp.galleries.all().count(), 2)
        self.assertFalse(bp.galleries.filter(name="old man").exists())
        self.assertEqual(bd.galleries.all().count(), 3)
        self.assertTrue(bd.galleries.filter(name="old man").exists())

        # publish the changed draft; change should now in published version
        bd.publish()
        bp2 = klass.normal.get(object_id=obp.object_id, state=models.Book.PUBLISHED)
        klass.normal.filter(object_id=obp.object_id, state=models.Book.DRAFT).exists()
        ba2 = klass.normal.get(object_id=obp.object_id, state=models.Book.ARCHIVED)
        self.assertEqual(models.Gallery.objects.all().count(), 8)
        self.assertEqual(bp2.galleries.all().count(), 3)
        self.assertTrue(bp2.galleries.filter(name="old man").exists())
        # meanwhile, published get archived
        self.assertEqual(ba2.vid, bp.vid)

        # revert archived back to draft
        ba2.make_draft()
        self.assertEqual(klass.normal.all().count(), 3)
        self.assertEqual(models.Gallery.objects.all().count(), 7)
        bp3 = klass.normal.get(object_id=obp.object_id, state=models.Book.PUBLISHED)
        bd3 = klass.normal.get(object_id=obp.object_id, state=models.Book.DRAFT)
        ba3 = klass.normal.get(object_id=obp.object_id, state=models.Book.ARCHIVED)
        self.assertEqual(bd3.galleries.all().count(), 2)
        self.assertFalse(bd3.galleries.filter(name="old man").exists())

        # remove one gallery item from draft
        g = bd3.galleries.filter(name="Gallery1")
        self.assertEqual(len(g), 1)
        bd3.galleries.remove(g[0])
        self.assertEqual(models.Gallery.objects.all().count(), 7)
        self.assertEqual(bd3.galleries.all().count(), 1)
        self.assertEqual(bp3.galleries.all().count(), 3)
        self.assertEqual(ba3.galleries.all().count(), 2)

        # publish the draft, we now have 1 published, 1 draft, and 2 archived
        bd3.publish()
        self.assertEqual(klass.normal.all().count(), 4)
        self.assertEqual(models.Gallery.objects.all().count(), 8)
        bp4 = klass.normal.get(object_id=obp.object_id, state=models.Book.PUBLISHED)
        bd4 = klass.normal.get(object_id=obp.object_id, state=models.Book.DRAFT)
        ba4 = klass.normal.get(object_id=obp.object_id, vid=bp3.vid)
        # now both the draft and published should have only 1 gallery item
        self.assertEqual(bp4.galleries.all().count(), 1)
        self.assertFalse(bp4.galleries.filter(name="Gallery1").exists())
        self.assertEqual(bd4.galleries.all().count(), 1)
        self.assertFalse(bd4.galleries.filter(name="Gallery1").exists())

        # revert back to the new archived status (ba4 is actually bp3)
        ba4.make_draft()
        self.assertEqual(klass.normal.all().count(), 4)
        self.assertEqual(models.Gallery.objects.all().count(), 10)

        bp5 = klass.normal.get(object_id=obp.object_id, state=models.Book.PUBLISHED)
        bd5 = klass.normal.get(object_id=obp.object_id, state=models.Book.DRAFT)
        # now the draft should havd 3 gallery items
        self.assertEqual(bd5.galleries.all().count(), 3)
        self.assertTrue(bd5.galleries.filter(name="Gallery1").exists())
        self.assertTrue(bd5.galleries.filter(name="old man").exists())
        # published is not affected
        self.assertEqual(bp5.galleries.all().count(), 1)
        self.assertTrue(bp5.galleries.filter(name="Gallery2").exists())

    def testMakeDraftWithoutDraft(self):
        # it is ok for published book to make draft, when there is no draft.
        book = models.Book.objects.get(vid=1)
        klass = book.get_version_class()
        book = klass.normal.get(vid=1)

        book.publish()
        draft = klass.normal.get(object_id=book.object_id, state=models.Book.DRAFT)
        draft.delete()

        self.assertTrue(
            klass.normal.filter(
                object_id=book.object_id, state=models.Book.PUBLISHED
            ).exists()
        )
        book.make_draft()
        self.assertEqual(klass.normal.all().count(), 2)

    def testPurgeArchive(self):
        # create a few ARCHIVED versions
        for i in range(0, 10):
            book = models.Book.objects.get(vid=1)
            book.publish()

        # the underlying versions
        klass = book.get_version_class()
        self.assertEqual(klass.normal.filter(state=models.Book.ARCHIVED).count(), 9)
        self.assertEqual(klass.normal.filter(state=models.Book.DRAFT).count(), 1)
        self.assertEqual(klass.normal.filter(state=models.Book.PUBLISHED).count(), 1)

        # make sure only NUM_KEEP_ARCHIVED (5) number of ARCHIVED are kept
        book.purge_archives()
        self.assertEqual(klass.normal.filter(state=models.Book.ARCHIVED).count(), 5)
        self.assertEqual(models.Gallery.objects.all().count(), 14)

    def testStatus1(self):
        book = models.Book.objects.get(vid=1)
        self.assertEqual(book.state, models.Book.DRAFT)
        self.assertEqual(book.status_line(), "Draft saved: 08/30/2012")

        # schedule to publish in future. should get status as Scheduled.
        book.publish(when=timezone.now() + datetime.timedelta(days=7))
        draft = models.Book.objects.get(vid=1)

        self.assertEqual(draft.last_save, draft.last_scheduled)
        self.assertFalse(draft.v_last_save)  # v_last_save empty
        status = (
            "Publish Scheduled: "
            f"{formats.date_format(draft.date_published, 'SHORT_DATE_FORMAT')}"
        )
        self.assertEqual(status, draft.status_line())

        # publish it now. should get status as Published
        when = timezone.now()
        book = models.Book.objects.get(vid=1)
        book.publish(when=when)
        draft2 = models.Book.objects.get(vid=1)

        self.assertEqual(draft2.last_save, draft2.last_scheduled)
        self.assertEqual(draft2.last_save, draft2.v_last_save)
        status = (
            "Published: "
            f"{formats.date_format(draft2.date_published, 'SHORT_DATE_FORMAT')}"
        )
        self.assertEqual(status, draft2.status_line())
        self.assertEqual(draft2.date_published, when)

        # schedule to publish in future again. should get status as Scheduled
        book = models.Book.objects.get(vid=1)
        book.publish(when=timezone.now() + datetime.timedelta(days=7))
        draft = models.Book.objects.get(vid=1)
        self.assertEqual(draft.last_save, draft.last_scheduled)
        self.assertTrue(draft.last_scheduled > draft.v_last_save)
        status = (
            "Publish Scheduled: "
            f"{formats.date_format(draft.date_published, 'SHORT_DATE_FORMAT')}"
        )
        self.assertEqual(status, draft.status_line())

    def testStatus2(self):
        book = models.Book.objects.get(vid=1)
        self.assertEqual(book.state, models.Book.DRAFT)
        self.assertEqual(book.status_line(), "Draft saved: 08/30/2012")

        book.publish()
        draft = models.Book.objects.get(vid=1)
        self.assertEqual(book.v_last_save, draft.v_last_save)
        self.assertEqual(draft.last_save, draft.last_scheduled)
        self.assertEqual(draft.last_save, draft.v_last_save)
        status = (
            "Published: "
            f"{formats.date_format(draft.last_save, 'SHORT_DATE_FORMAT')}"
        )
        self.assertEqual(status, book.status_line())

    def testSwitchSchema(self):
        book = models.Book.objects.get(vid=1)
        book.publish()
        self.assertEqual(models.Book.normal.all().count(), 1)
        with manager.SwitchSchema("public"):
            self.assertEqual(models.Book.normal.all().count(), 2)

        self.assertEqual(models.Book.normal.all().count(), 1)
        manager.deactivate()
        self.assertEqual(models.Book.normal.all().count(), 2)

    def testVersionedUnique(self):
        """
        use versioned_unique to force unique on field value
        """
        self.assertFalse(hasattr(models.Author, "versioned_unique"))
        setattr(models.Author, "versioned_unique", ["name"])
        self.assertEqual(models.Author.versioned_unique, ["name"])
        a1 = models.Author.objects.get(vid=1)

        # should fail on second(duplicate) instance
        a2 = models.Author(name=a1.name)
        with self.assertRaises(ValidationError):
            a2.validate_unique()

        # multiple versions are not affected (can co-exists)
        klass = a1.get_version_class()
        self.assertEqual(klass.normal.filter(object_id=a1.object_id).count(), 1)
        a1.publish()
        self.assertEqual(klass.normal.filter(object_id=a1.object_id).count(), 2)
        self.assertTrue(
            klass.normal.filter(object_id=a1.object_id, state=a1.DRAFT).exists()
        )
        self.assertTrue(
            klass.normal.filter(object_id=a1.object_id, state=a1.PUBLISHED).exists()
        )
        a1.validate_unique()

    def testCustomBaseModel(self):
        """
        custom base_model; defined by _base_model in Meta;
        should turn on 'should_save_base' to save fields in _base_model.
        """
        gun = models.Gun()
        self.assertEqual(gun._meta._base_model, models.CustomModel)

        # should have all the fields, including the one from _base_model
        self.assertTrue(hasattr(gun, "created_date"))
        self.assertTrue(hasattr(gun, "v_last_save"))
        self.assertTrue(hasattr(gun, "reg_number"))
        self.assertTrue(hasattr(gun, "is_published"))
        self.assertFalse(hasattr(gun, "none_exist_field"))

        self.assertTrue(hasattr(gun, "should_save_base"))

        self.assertEqual(gun.should_save_base, False)

        # should be created normally
        gun.name = "AK47"
        gun.save()

        gun = models.Gun.objects.get(name="AK47")
        self.assertEqual(gun.object_id, 1)
        self.assertFalse(gun.reg_number)

        self.assertEqual(gun.should_save_base, False)
        gun.reg_number = "A1234"
        gun.save()
        gun = models.Gun.objects.get(vid=1)
        # not get saved
        self.assertFalse(gun.reg_number)

        gun.should_save_base = True
        gun.reg_number = "B3210"
        gun.save()
        gun = models.Gun.objects.get(vid=1)
        # should get saved now, since should_save_base is on
        self.assertEqual(gun.reg_number, "B3210")


class TransactionSwitchTests(TestCase):
    fixtures = ("test_data.json",)

    def setUp(self):
        manager.activate("draft")

    def tearDown(self):
        manager.deactivate()

    def testDeleteWithNormal(self):
        author = models.Author.objects.get(vid=1)
        author_id = author.pk
        author.publish()

        cartoon = models.Cartoon.objects.get(vid=1)
        self.assertEqual(cartoon.author, author)
        cartoon.publish()

        book = models.Book.objects.get(vid=1)
        book.publish()

        book2 = models.Book(name="test", author_id=2)
        book2.save()

        with manager.SwitchSchema("public"):
            self.assertEqual(models.Author.normal.filter(associates__pk=2).count(), 2)
            self.assertEqual(models.Cartoon.normal.filter(author=author).count(), 2)
            self.assertEqual(
                models.Cartoon.normal.filter(author__isnull=True).count(), 0
            )

        # Switch to draft mode to restrict what the ORM
        # will normally see
        with manager.SwitchSchema("draft"):
            self.assertEqual(models.Book.objects.filter(author=author).count(), 1)
            self.assertEqual(
                models.Gallery.objects.filter(book__author=author).count(), 2
            )
            self.assertEqual(
                models.Review.objects.filter(book__author=author).count(), 2
            )

            book = models.Book.objects.get(vid=1)
            self.assertEqual(book.author, author)
            author.delete()

        with manager.SwitchSchema("public"):
            self.assertEqual(models.Author.normal.filter(associates__pk=2).count(), 0)
            self.assertFalse(models.Author.normal.filter(pk=author_id).exists())
            self.assertEqual(
                models.Cartoon.normal.filter(author_id=author_id).count(), 0
            )
            self.assertEqual(
                models.Cartoon.normal.filter(author__isnull=True).count(), 2
            )
            self.assertEqual(models.Book.normal.all().count(), 1)
            self.assertEqual(models.Review.objects.all().count(), 0)
            self.assertEqual(models.Gallery.objects.all().count(), 0)

