# (c) cavaliba.com - tests / schema / is_bigset persisted by Schema.save()

from unittest.mock import patch

from django.core.cache import cache as cache_django
from django.test import TestCase

import app_home.cache as cache
from app_data.data import update_bigset
from app_data.loader import load_broker
from app_data.models import DataClass, DataInstance
from app_data.schema import Schema
from app_home.configuration import set_configuration
from tests import helper

SCHEMA = "tbig"


class TestSchemaIsBigsetPersisted(TestCase):
    def setUp(self):
        cache.clear()
        cache_django.clear()
        helper.add_schema(SCHEMA, displayname="Bigset Schema")
        # keep the threshold low so the test stays fast
        set_configuration(
            appname="data", keyname="DATA_BIGSET_SIZE",
            value=5, description="", page="", order=100,
        )  # fmt: skip
        cache.clear()

    def _seed(self, count):
        DataInstance.objects.bulk_create(
            [DataInstance(classname=SCHEMA, keyname=f"obj{i:04d}") for i in range(count)]
        )

    def _db_flag(self):
        return DataClass.objects.get(keyname=SCHEMA).is_bigset

    def test_save_writes_is_bigset_to_db(self):
        schema = Schema.from_name(SCHEMA)
        schema.is_bigset = True
        schema.save()
        self.assertTrue(self._db_flag())

    def test_save_writes_is_bigset_false(self):
        DataClass.objects.filter(keyname=SCHEMA).update(is_bigset=True)
        cache.clear()
        schema = Schema.from_name(SCHEMA)
        schema.is_bigset = False
        schema.save()
        self.assertFalse(self._db_flag())

    def test_default_is_false(self):
        self.assertFalse(self._db_flag())

    def test_update_bigset_sets_the_flag_above_threshold(self):
        self._seed(10)
        update_bigset()
        self.assertTrue(self._db_flag())

    def test_update_bigset_leaves_small_schema_alone(self):
        self._seed(2)
        update_bigset()
        self.assertFalse(self._db_flag())

    def test_update_bigset_clears_the_flag_when_count_drops(self):
        self._seed(10)
        update_bigset()
        self.assertTrue(self._db_flag())

        DataInstance.objects.filter(classname=SCHEMA).exclude(keyname="obj0000").delete()
        cache.clear()
        cache_django.clear()
        update_bigset()
        self.assertFalse(self._db_flag())

    def test_update_bigset_clears_the_flag_when_all_instances_are_gone(self):
        self._seed(10)
        update_bigset()
        DataInstance.objects.filter(classname=SCHEMA).delete()
        cache.clear()
        cache_django.clear()
        update_bigset()
        self.assertFalse(self._db_flag())

    def test_update_bigset_at_threshold_is_not_bigset(self):
        # strictly greater than the threshold, so exactly DATA_BIGSET_SIZE stays inline
        self._seed(5)
        update_bigset()
        self.assertFalse(self._db_flag())

    def test_update_bigset_is_idempotent(self):
        self._seed(10)
        update_bigset()
        cache.clear()
        cache_django.clear()
        update_bigset()
        self.assertTrue(self._db_flag())

    def test_update_bigset_does_not_rewrite_unchanged_schemas(self):
        # Schema.save() rewrites every field row and flushes caches, so a converged
        # run must write nothing at all. (The first run does write: the lowered
        # threshold pushes several builtin schemas over it too.)
        self._seed(2)
        update_bigset()
        cache.clear()
        cache_django.clear()

        with patch.object(Schema, "save", autospec=True) as mocked:
            update_bigset()
        self.assertEqual(mocked.call_count, 0)

    def test_update_bigset_saves_only_the_schema_that_changed(self):
        self._seed(2)
        update_bigset()
        cache.clear()
        cache_django.clear()

        # push this one schema over the threshold, leaving every other converged
        DataInstance.objects.bulk_create(
            [DataInstance(classname=SCHEMA, keyname=f"more{i:04d}") for i in range(10)]
        )
        with patch.object(Schema, "save", autospec=True) as mocked:
            update_bigset()
        saved = {call.args[0].classname for call in mocked.call_args_list}
        self.assertEqual(saved, {SCHEMA})

    def test_flag_is_readable_back_through_from_name(self):
        self._seed(10)
        update_bigset()
        cache.clear()
        cache_django.clear()
        self.assertTrue(Schema.from_name(SCHEMA).is_bigset)

    def test_yaml_reload_of_existing_schema_preserves_the_flag(self):
        # a schema YAML carries no is_bigset key: an "update"/"create" reload must not
        # clobber the computed flag (from_name() -> from_obj() reloads it first)
        self._seed(10)
        update_bigset()
        cache.clear()
        cache_django.clear()

        datalist = [
            {
                "classname": "_schema",
                "keyname": SCHEMA,
                "_action": "update",
                "displayname": "Bigset Schema renamed",
            }
        ]
        load_broker(datalist=datalist, aaa={"perms": ["p_schema_write"]})

        self.assertTrue(self._db_flag())
        self.assertEqual(DataClass.objects.get(keyname=SCHEMA).displayname, "Bigset Schema renamed")
