# (c) cavaliba.com - tests / loader append/unappend actions

from django.test import TestCase

import app_home.cache as cache
from app_data.data import Instance
from app_data.loader import load_broker
from tests.helper import add_instance, add_schema

AAA = {"perms": ["p_data_admin"]}
AAA_NO_PERM = {"perms": []}


class TestLoaderActionAppend(TestCase):
    """append/unappend union/subtract values on multivalued fields instead of
    replacing them wholesale (which is what "update" does); single-valued fields
    fall back to plain "update" semantics for both actions."""

    def setUp(self):
        cache.clear()

        add_schema(
            classname="appendtest",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
                "tags": {
                    "dataformat": "string",
                    "displayname": "Tags",
                    "cardinal_max": 0,
                },
            },
        )

    def _tags(self, keyname):
        instance = Instance.from_keyname(classname="appendtest", keyname=keyname, expand=False)
        return instance.fields["tags"].value

    def test_append_onto_empty_field_sets_it(self):
        add_instance(classname="appendtest", keyname="obj01")
        result = load_broker(
            [
                {
                    "classname": "appendtest",
                    "_action": "append",
                    "keyname": "obj01",
                    "tags": ["a", "b"],
                }
            ],
            aaa=AAA,
        )
        self.assertEqual(result["count_ok"], 1)
        self.assertEqual(self._tags("obj01"), ["a", "b"])

    def test_append_preserves_existing_values(self):
        add_instance(classname="appendtest", keyname="obj02", fields={"tags": ["a"]})
        result = load_broker(
            [
                {
                    "classname": "appendtest",
                    "_action": "append",
                    "keyname": "obj02",
                    "tags": ["b", "c"],
                }
            ],
            aaa=AAA,
        )
        self.assertEqual(result["count_ok"], 1)
        self.assertEqual(self._tags("obj02"), ["a", "b", "c"])

    def test_append_is_idempotent_no_duplicates(self):
        add_instance(classname="appendtest", keyname="obj03", fields={"tags": ["a", "b"]})
        result = load_broker(
            [{"classname": "appendtest", "_action": "append", "keyname": "obj03", "tags": ["b"]}],
            aaa=AAA,
        )
        self.assertEqual(result["count_ok"], 1)
        self.assertEqual(self._tags("obj03"), ["a", "b"])

    def test_unappend_removes_subset(self):
        add_instance(classname="appendtest", keyname="obj04", fields={"tags": ["a", "b", "c"]})
        result = load_broker(
            [{"classname": "appendtest", "_action": "unappend", "keyname": "obj04", "tags": ["b"]}],
            aaa=AAA,
        )
        self.assertEqual(result["count_ok"], 1)
        self.assertEqual(self._tags("obj04"), ["a", "c"])

    def test_unappend_missing_value_is_noop(self):
        add_instance(classname="appendtest", keyname="obj05", fields={"tags": ["a"]})
        result = load_broker(
            [
                {
                    "classname": "appendtest",
                    "_action": "unappend",
                    "keyname": "obj05",
                    "tags": ["zzz"],
                }
            ],
            aaa=AAA,
        )
        self.assertEqual(result["count_ok"], 1)
        self.assertEqual(self._tags("obj05"), ["a"])

    def test_append_on_single_valued_field_replaces(self):
        add_instance(classname="appendtest", keyname="obj06", fields={"mystring": "old"})
        result = load_broker(
            [
                {
                    "classname": "appendtest",
                    "_action": "append",
                    "keyname": "obj06",
                    "mystring": "new",
                }
            ],
            aaa=AAA,
        )
        self.assertEqual(result["count_ok"], 1)
        instance = Instance.from_keyname(classname="appendtest", keyname="obj06", expand=False)
        self.assertEqual(instance.fields["mystring"].value, ["new"])

    def test_unappend_on_single_valued_field_replaces(self):
        add_instance(classname="appendtest", keyname="obj07", fields={"mystring": "old"})
        result = load_broker(
            [
                {
                    "classname": "appendtest",
                    "_action": "unappend",
                    "keyname": "obj07",
                    "mystring": "new",
                }
            ],
            aaa=AAA,
        )
        self.assertEqual(result["count_ok"], 1)
        instance = Instance.from_keyname(classname="appendtest", keyname="obj07", expand=False)
        self.assertEqual(instance.fields["mystring"].value, ["new"])

    def test_append_on_nonexistent_instance_fails(self):
        result = load_broker(
            [{"classname": "appendtest", "_action": "append", "keyname": "missing", "tags": ["a"]}],
            aaa=AAA,
        )
        self.assertEqual(result["count_ko"], 1)
        self.assertIsNone(
            Instance.from_keyname(classname="appendtest", keyname="missing", expand=False)
        )

    def test_unappend_on_nonexistent_instance_fails(self):
        result = load_broker(
            [
                {
                    "classname": "appendtest",
                    "_action": "unappend",
                    "keyname": "missing",
                    "tags": ["a"],
                }
            ],
            aaa=AAA,
        )
        self.assertEqual(result["count_ko"], 1)
        self.assertIsNone(
            Instance.from_keyname(classname="appendtest", keyname="missing", expand=False)
        )

    def test_append_without_update_permission_is_denied(self):
        add_instance(classname="appendtest", keyname="obj08", fields={"tags": ["a"]})
        result = load_broker(
            [{"classname": "appendtest", "_action": "append", "keyname": "obj08", "tags": ["b"]}],
            aaa=AAA_NO_PERM,
        )
        self.assertEqual(result["count_ko"], 1)
        self.assertEqual(self._tags("obj08"), ["a"])

    def test_csv_caret_joined_cell_appends_multiple_values(self):
        from app_data.loader import load_file_csv

        add_instance(classname="appendtest", keyname="obj09", fields={"tags": ["a"]})

        import os
        import tempfile

        fd, path = tempfile.mkstemp(suffix=".csv")
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            f.write("keyname,tags,_action\nobj09,b^c,append\n")
        try:
            datalist = load_file_csv(filename=path, schema_name="appendtest")
        finally:
            os.remove(path)

        result = load_broker(datalist=datalist, aaa=AAA)
        self.assertEqual(result["count_ok"], 1)
        self.assertEqual(self._tags("obj09"), ["a", "b", "c"])
