# (c) cavaliba.com - tests / fieldtypes / schema field bigset detection & option loading

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 Instance
from app_data.models import DataClass, DataInstance
from app_home.configuration import set_configuration
from tests import helper

AAA = {"username": "testuser", "perms": ["p_data_admin"]}

TARGET = "tfstarget"
OWNER = "tfsowner"
THRESHOLD = 5


class TestFieldSchemaBigsetFallback(TestCase):
    def setUp(self):
        cache.clear()
        cache_django.clear()
        set_configuration(
            appname="data", keyname="DATA_BIGSET_SIZE",
            value=THRESHOLD, description="", page="", order=100,
        )  # fmt: skip
        cache.clear()

        helper.add_schema(TARGET, displayname="Target")
        helper.add_schema(
            OWNER,
            displayname="Owner",
            field_definition={
                "ref": {"dataformat": "schema", "dataformat_ext": TARGET, "cardinal_max": 0}
            },
        )

    def _seed_targets(self, count, first_disabled=False):
        rows = [
            DataInstance(classname=TARGET, keyname=f"t{i:04d}", displayname=f"Target {i}")
            for i in range(count)
        ]
        if first_disabled and rows:
            rows[0].is_enabled = False
        DataInstance.objects.bulk_create(rows)

    def _datapoint(self, selected=None):
        owner = Instance(classname=OWNER)
        payload = {"classname": OWNER, "keyname": "own01"}
        if selected:
            payload["ref"] = selected
        owner.merge_import(payload, aaa=AAA)
        owner.save(aaa=AAA, action="create")

        cache.clear()
        cache_django.clear()
        owner = Instance.from_keyname(classname=OWNER, keyname="own01")
        return owner.fields["ref"].get_datapoint_ui_edit()

    # --- small target schema: inline options ---

    def test_small_schema_is_not_bigset(self):
        self._seed_targets(3)
        datapoint = self._datapoint()
        self.assertFalse(datapoint["bigset"])
        self.assertEqual(len(datapoint["value"]), 3)

    def test_small_schema_marks_selected_values(self):
        self._seed_targets(3)
        datapoint = self._datapoint(selected=["t0001"])
        selected = [i["key"] for i in datapoint["value"] if i["selected"]]
        self.assertEqual(selected, ["t0001"])

    def test_small_schema_exposes_keyname_and_displayname(self):
        self._seed_targets(2)
        datapoint = self._datapoint()
        item = datapoint["value"][0]
        self.assertEqual(item["key"], "t0000")
        self.assertEqual(item["display"], "Target 0")

    def test_small_schema_reports_is_enabled(self):
        self._seed_targets(3, first_disabled=True)
        datapoint = self._datapoint()
        flags = {i["key"]: i["is_enabled"] for i in datapoint["value"]}
        self.assertFalse(flags["t0000"])
        self.assertTrue(flags["t0001"])

    def test_exactly_threshold_stays_inline(self):
        # strictly greater than the threshold, so exactly THRESHOLD is not a bigset
        self._seed_targets(THRESHOLD)
        datapoint = self._datapoint()
        self.assertFalse(datapoint["bigset"])
        self.assertEqual(len(datapoint["value"]), THRESHOLD)

    # --- stale flag: detected from the row count, not from is_bigset ---

    def test_stale_false_flag_is_overridden_by_row_count(self):
        # is_bigset still False (update_bigset() has not run since the import)
        self._seed_targets(THRESHOLD + 20)
        self.assertFalse(DataClass.objects.get(keyname=TARGET).is_bigset)

        datapoint = self._datapoint(selected=["t0002"])
        self.assertTrue(datapoint["bigset"])

    def test_stale_flag_does_not_load_every_instance(self):
        self._seed_targets(THRESHOLD + 20)
        datapoint = self._datapoint(selected=["t0002"])
        # only the selected value, never the 25 candidates
        self.assertEqual([i["key"] for i in datapoint["value"]], ["t0002"])

    def test_bigset_marks_every_returned_value_selected(self):
        self._seed_targets(THRESHOLD + 20)
        datapoint = self._datapoint(selected=["t0002", "t0003"])
        self.assertEqual(len(datapoint["value"]), 2)
        self.assertTrue(all(i["selected"] for i in datapoint["value"]))

    def test_bigset_with_no_selection_returns_no_options(self):
        self._seed_targets(THRESHOLD + 20)
        datapoint = self._datapoint()
        self.assertTrue(datapoint["bigset"])
        self.assertEqual(datapoint["value"], [])

    # --- flag set: honoured without counting ---

    def test_flagged_schema_takes_the_bigset_path_even_when_small(self):
        self._seed_targets(2)
        DataClass.objects.filter(keyname=TARGET).update(is_bigset=True)
        cache.clear()
        cache_django.clear()
        datapoint = self._datapoint(selected=["t0001"])
        self.assertTrue(datapoint["bigset"])
        self.assertEqual([i["key"] for i in datapoint["value"]], ["t0001"])

    # --- dangling dataformat_ext ---

    def test_deleted_target_schema_does_not_raise(self):
        self._seed_targets(3)
        DataClass.objects.filter(keyname=TARGET).delete()
        cache.clear()
        cache_django.clear()
        # Schema.from_name() now returns None: must not AttributeError
        datapoint = self._datapoint()
        self.assertFalse(datapoint["bigset"])
        self.assertEqual(datapoint["dataformat_ext"], TARGET)

    def test_empty_dataformat_ext_does_not_raise(self):
        helper.add_schema(
            "tfsempty",
            displayname="Empty ext",
            field_definition={"ref": {"dataformat": "schema", "dataformat_ext": ""}},
        )
        owner = Instance(classname="tfsempty")
        owner.merge_import({"classname": "tfsempty", "keyname": "own02"}, aaa=AAA)
        owner.save(aaa=AAA, action="create")
        datapoint = owner.fields["ref"].get_datapoint_ui_edit()
        self.assertFalse(datapoint["bigset"])
        self.assertEqual(datapoint["value"], [])
