# (c) cavaliba.com - tests / data / fields

from django.test import TestCase

import app_home.cache as cache
from app_data.data import Instance
from app_data.fieldtypes.field_float import FieldFloat
from app_data.fieldtypes.field_int import FieldInt
from app_data.fieldtypes.field_ipv4 import FieldIPV4
from tests.helper import add_instance, add_schema

ENUMERATE_TEST_CONTENT = (
    '- value: "A"\n'
    '  widget: "green_circle"\n'
    "  enumstring: string AAA\n"
    "  enumint: int 111\n"
    "  enumfloat: float 111.1\n"
    "  enumboolean: boolean no\n"
    "  enumdate: date 2026-01-15\n"
    '- value: "B"\n'
    '  widget: "yellow_circle"\n'
    '- value: "C"\n'
    '  widget: "orange_circle"\n'
)


class TestDataFieldTypes(TestCase):
    def setUp(self):
        cache.clear()

        add_instance(
            classname="_enumerate",
            keyname="enumerate_test",
            fields={
                "content": ENUMERATE_TEST_CONTENT,
            },
        )

        add_schema(
            classname="test0",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
            },
        )
        add_instance(classname="test0", keyname="test0-01")
        add_instance(classname="test0", keyname="test0-02")
        add_instance(classname="test0", keyname="test0-03")

        add_schema(
            classname="test1",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
                "mystring_default": {
                    "dataformat": "string",
                    "displayname": "MyString Default",
                    "default_value": "hello",
                },
                "myint": {"dataformat": "int", "displayname": "MyInt"},
                "myint_default": {
                    "dataformat": "int",
                    "displayname": "MyInt Default",
                    "default_value": "11",
                },
                "myint_default_multi": {
                    "dataformat": "int",
                    "displayname": "MyInt Default Multi",
                    "default_value": "11 22",
                    "cardinal_max": 0,
                },
                "myfloat": {"dataformat": "float", "displayname": "MyFloat"},
                "myfloat_default": {
                    "dataformat": "float",
                    "displayname": "MyFloat Default",
                    "default_value": "11.11",
                },
                "myfloat_default_multi": {
                    "dataformat": "float",
                    "displayname": "MyFloat Default Multi",
                    "default_value": "11.11 22.22",
                    "cardinal_max": 0,
                },
                "myboolean": {"dataformat": "boolean", "displayname": "MyBoolean"},
                "myboolean_default_true": {
                    "dataformat": "boolean",
                    "displayname": "MyBoolean Default True",
                    "default_value": "True",
                },
                "myboolean_default_false": {
                    "dataformat": "boolean",
                    "displayname": "MyBoolean Default False",
                    "default_value": "False",
                },
                "mydate": {"dataformat": "date", "displayname": "MyDate"},
                "myipv4": {"dataformat": "ipv4", "displayname": "MyIPV4"},
                "my_schema_test0": {
                    "dataformat": "schema",
                    "displayname": "Schema Test0",
                    "dataformat_ext": "test0",
                },
                "my_schema_test0_multi": {
                    "dataformat": "schema",
                    "displayname": "Schema Test0 Multi",
                    "dataformat_ext": "test0",
                    "cardinal_max": 0,
                },
                "my_schema_self": {
                    "dataformat": "schema",
                    "displayname": "Schema Self",
                    "dataformat_ext": "test1",
                },
                "my_enumerate_abcde": {
                    "dataformat": "enumerate",
                    "displayname": "Enumerate ABCDE",
                    "dataformat_ext": "enumerate_test",
                },
                "my_enumerate_abcde_multi": {
                    "dataformat": "enumerate",
                    "displayname": "Enumerate Multi ABCDE",
                    "dataformat_ext": "enumerate_test",
                    "cardinal_max": 0,
                },
                "my_enumerate_subfields": {
                    "dataformat": "enumerate",
                    "displayname": "Enumerate Subfields",
                    "dataformat_ext": "enumerate_test",
                },
            },
        )
        add_instance(
            classname="test1",
            keyname="test1-01",
            fields={
                "mystring": "data for test1-01",
                "myint": 11,
                "myfloat": 27.19,
                "myboolean": True,
                "mydate": "2024-11-01",
                "myipv4": "10.1.2.3",
                "my_schema_test0": "test0-01",
                "my_schema_test0_multi": ["test0-02", "test0-03"],
                "my_schema_self": "test1-03",
                "my_enumerate_abcde": "A",
                "my_enumerate_abcde_multi": ["A", "B", "C"],
                "my_enumerate_subfields": "A",
            },
        )
        add_instance(classname="test1", keyname="test1-03")

        add_schema(
            classname="test2",
            field_definition={
                "test1": {
                    "dataformat": "schema",
                    "displayname": "Test1 Object",
                    "dataformat_ext": "test1 mystring myint myfloat myboolean",
                },
            },
        )
        add_instance(classname="test2", keyname="test2-01", fields={"test1": "test1-01"})

        add_schema(
            classname="test3",
            field_definition={
                "test2": {
                    "dataformat": "schema",
                    "displayname": "Test2 Object",
                    "dataformat_ext": "test2",
                },
                "_test1": {
                    "dataformat": "external",
                    "displayname": "Test1 (external)",
                    "dataformat_ext": "test2 test1 mystring myint myfloat myboolean",
                },
            },
        )
        add_instance(classname="test3", keyname="test3-01", fields={"test2": "test2-01"})

        add_schema(
            classname="test4",
            field_definition={
                "test3": {
                    "dataformat": "schema",
                    "displayname": "Test3 Object",
                    "dataformat_ext": "test3",
                },
                "_test1": {
                    "dataformat": "external",
                    "displayname": "Test1 (external)",
                    "dataformat_ext": "test3 test1 displayname is_enabled mystring myint myfloat myboolean",
                },
            },
        )
        add_instance(classname="test4", keyname="test4-01", fields={"test3": "test3-01"})

    def test_field_values(self):
        instance = Instance.from_keyname(classname="test1", keyname="test1-01", expand=False)
        self.assertIsNotNone(instance)
        self.assertEqual(instance.fields["mystring"].get_value(), ["data for test1-01"])
        self.assertEqual(instance.fields["myint"].get_value(), [11])
        self.assertEqual(instance.fields["myfloat"].get_value(), [27.19])
        self.assertEqual(instance.fields["myboolean"].get_value(), [True])
        self.assertEqual(instance.fields["mydate"].get_value(), ["2024-11-01"])
        self.assertEqual(instance.fields["myipv4"].get_value(), ["10.1.2.3"])
        self.assertEqual(instance.fields["my_schema_test0"].get_value(), ["test0-01"])
        self.assertEqual(
            instance.fields["my_schema_test0_multi"].get_value(), ["test0-02", "test0-03"]
        )
        self.assertEqual(instance.fields["my_schema_self"].get_value(), ["test1-03"])

    def test_default_value(self):
        instance = Instance(classname="test1")
        self.assertEqual(instance.fields["mystring_default"].get_value(), ["hello"])
        self.assertEqual(instance.fields["myint_default"].get_value(), [11])
        self.assertEqual(instance.fields["myint_default_multi"].get_value(), [11, 22])
        self.assertEqual(instance.fields["myfloat_default"].get_value(), [11.11])
        self.assertEqual(instance.fields["myfloat_default_multi"].get_value(), [11.11, 22.22])

    def test_boolean(self):
        instance = Instance(classname="test1")

        a = instance.fields["myboolean_default_true"].get_first_value()
        self.assertEqual(type(a), bool)
        self.assertTrue(a)

        b = instance.fields["myboolean_default_false"].get_first_value()
        self.assertEqual(type(b), bool)
        self.assertFalse(b)

    def test_external_no_expand(self):
        instance = Instance.from_keyname(classname="test4", keyname="test4-01", expand=False)
        self.assertIsNotNone(instance)
        self.assertIsNotNone(instance.fields["_test1"])
        self.assertFalse("test1" in instance.fields)

    def test_external_expand(self):
        instance = Instance.from_keyname(classname="test4", keyname="test4-01", expand=True)
        self.assertIsNotNone(instance)
        self.assertIsNotNone(instance.fields["_test1"])
        self.assertIsNotNone(instance.fields["test1"])
        self.assertIsNotNone(instance.fields["test1__mystring"])
        self.assertIsNotNone(instance.fields["test1__myint"])
        self.assertIsNotNone(instance.fields["test1__myfloat"])
        self.assertIsNotNone(instance.fields["test1__myboolean"])
        self.assertEqual(instance.fields["test1__myint"].get_value(), [11])
        self.assertEqual(instance.fields["test1__myfloat"].get_value(), [27.19])
        self.assertEqual(instance.fields["test1__myboolean"].get_value(), [True])
        self.assertEqual(instance.fields["test1__mystring"].get_value(), ["data for test1-01"])

    def test_enumerate_no_expand(self):
        instance = Instance.from_keyname(classname="test1", keyname="test1-01", expand=False)
        self.assertIsNotNone(instance)
        self.assertIsNotNone(instance.fields["my_enumerate_abcde"])
        self.assertEqual(instance.fields["my_enumerate_abcde"].get_value(), ["A"])
        self.assertIsNotNone(instance.fields["my_enumerate_abcde_multi"])
        self.assertTrue("A" in instance.fields["my_enumerate_abcde_multi"].get_value())
        self.assertTrue("B" in instance.fields["my_enumerate_abcde_multi"].get_value())
        self.assertTrue("C" in instance.fields["my_enumerate_abcde_multi"].get_value())

        self.assertIsNotNone(instance.fields["my_enumerate_subfields"])
        self.assertEqual(instance.fields["my_enumerate_subfields"].get_value(), ["A"])
        self.assertFalse("my_enumerate_subfields__widget" in instance.fields)
        self.assertFalse("my_enumerate_subfields__enumstring" in instance.fields)
        self.assertFalse("my_enumerate_subfields__enumint" in instance.fields)
        self.assertFalse("my_enumerate_subfields__enumfloat" in instance.fields)
        self.assertFalse("my_enumerate_subfields__enumboolean" in instance.fields)

    def test_enumerate_expand(self):
        instance = Instance.from_keyname(classname="test1", keyname="test1-01", expand=True)
        self.assertIsNotNone(instance)
        self.assertIsNotNone(instance.fields["my_enumerate_abcde"])
        self.assertEqual(instance.fields["my_enumerate_abcde"].get_value(), ["A"])
        self.assertIsNotNone(instance.fields["my_enumerate_abcde_multi"])
        self.assertTrue("A" in instance.fields["my_enumerate_abcde_multi"].get_value())
        self.assertTrue("B" in instance.fields["my_enumerate_abcde_multi"].get_value())
        self.assertTrue("C" in instance.fields["my_enumerate_abcde_multi"].get_value())

        self.assertIsNotNone(instance.fields["my_enumerate_subfields"])
        self.assertEqual(instance.fields["my_enumerate_subfields"].get_value(), ["A"])
        self.assertTrue("my_enumerate_subfields__widget" in instance.fields)
        self.assertTrue("my_enumerate_subfields__enumstring" in instance.fields)
        self.assertTrue("my_enumerate_subfields__enumint" in instance.fields)
        self.assertTrue("my_enumerate_subfields__enumfloat" in instance.fields)
        self.assertTrue("my_enumerate_subfields__enumboolean" in instance.fields)
        self.assertTrue("my_enumerate_subfields__enumdate" in instance.fields)

        self.assertEqual(instance.fields["my_enumerate_subfields__enumint"].get_value(), [111])
        self.assertEqual(instance.fields["my_enumerate_subfields__enumfloat"].get_value(), [111.1])
        self.assertEqual(instance.fields["my_enumerate_subfields__enumstring"].get_value(), ["AAA"])
        self.assertEqual(
            instance.fields["my_enumerate_subfields__enumboolean"].get_value(), [False]
        )
        self.assertEqual(
            instance.fields["my_enumerate_subfields__enumdate"].get_value(), ["2026-01-15"]
        )
        self.assertEqual(
            instance.fields["my_enumerate_subfields__widget"].get_value(), ["&#x1F7E2;"]
        )

    def test_int_valid(self):
        fieldschema = {"cardinal_min": 0, "cardinal_max": 1, "default_value": ""}

        f = FieldInt(fieldname="data", fieldschema=fieldschema, alljson={"data": [33]})
        self.assertEqual(f.is_valid(), True)
        f = FieldInt(fieldname="data", fieldschema=fieldschema, alljson={"data": [-3]})
        self.assertEqual(f.is_valid(), True)

        f = FieldInt(fieldname="data", fieldschema=fieldschema, alljson={"data": ["a"]})
        self.assertEqual(f.is_valid(), False)
        f = FieldInt(fieldname="data", fieldschema=fieldschema, alljson={"data": [33.4]})
        self.assertEqual(f.is_valid(), False)

    def test_float_valid(self):
        fieldschema = {"cardinal_min": 0, "cardinal_max": 1, "default_value": ""}

        f = FieldFloat(fieldname="data", fieldschema=fieldschema, alljson={"data": [33.2]})
        self.assertEqual(f.is_valid(), True)

        f = FieldFloat(fieldname="data", fieldschema=fieldschema, alljson={"data": ["a"]})
        self.assertEqual(f.is_valid(), False)

    def test_ipv4_valid(self):
        fieldschema = {"cardinal_min": 0, "cardinal_max": 1, "default_value": ""}

        f = FieldIPV4(fieldname="myip", fieldschema=fieldschema, alljson={"myip": ["10.1.2.3"]})
        self.assertEqual(f.is_valid(), True)
        f = FieldIPV4(fieldname="myip", fieldschema=fieldschema, alljson={"myip": ["0.0.0.0"]})
        self.assertEqual(f.is_valid(), True)
        f = FieldIPV4(
            fieldname="myip", fieldschema=fieldschema, alljson={"myip": ["255.255.255.255"]}
        )
        self.assertEqual(f.is_valid(), True)

        f = FieldIPV4(fieldname="myip", fieldschema=fieldschema, alljson={"myip": ["10.1.2.3/24"]})
        self.assertEqual(f.is_valid(), False)
        f = FieldIPV4(fieldname="myip", fieldschema=fieldschema, alljson={"myip": ["10.1.2.256"]})
        self.assertEqual(f.is_valid(), False)
        f = FieldIPV4(fieldname="myip", fieldschema=fieldschema, alljson={"myip": ["10.1.2"]})
        self.assertEqual(f.is_valid(), False)
        f = FieldIPV4(fieldname="myip", fieldschema=fieldschema, alljson={"myip": ["10.1.2.3.4"]})
        self.assertEqual(f.is_valid(), False)
        f = FieldIPV4(fieldname="myip", fieldschema=fieldschema, alljson={"myip": ["10.1.2.3/33"]})
        self.assertEqual(f.is_valid(), False)


class TestFieldUserMissingUser(TestCase):
    def setUp(self):
        cache.clear()

    def test_user_field_missing_user_shows_placeholder(self):
        add_schema(
            classname="schema_with_user",
            field_definition={
                "owner": {
                    "displayname": "Owner",
                    "dataformat": "user",
                }
            },
        )
        add_instance(
            classname="schema_with_user", keyname="item-01", fields={"owner": "nonexistent-login"}
        )

        instance = Instance.from_keyname(classname="schema_with_user", keyname="item-01")
        self.assertIsNotNone(instance)

        datapoint = instance.fields["owner"].get_datapoint_ui_detail()

        self.assertEqual(len(datapoint["value"]), 1)
        self.assertEqual(datapoint["value"][0]["display"], "?? - nonexistent-login")


class TestFieldSchemaMissingInstance(TestCase):
    def setUp(self):
        cache.clear()

    def test_schema_field_missing_instance_shows_placeholder(self):
        add_schema(classname="target_schema")
        add_schema(
            classname="source_schema",
            field_definition={
                "ref_field": {
                    "displayname": "Reference Field",
                    "dataformat": "schema",
                    "dataformat_ext": "target_schema",
                }
            },
        )
        add_instance(
            classname="source_schema", keyname="source-01", fields={"ref_field": "nonexistent-key"}
        )

        instance = Instance.from_keyname(classname="source_schema", keyname="source-01")
        self.assertIsNotNone(instance)

        datapoint = instance.fields["ref_field"].get_datapoint_ui_detail()

        self.assertEqual(len(datapoint["value"]), 1)
        self.assertEqual(datapoint["value"][0]["display"], "?? - nonexistent-key")
