# (c) cavaliba.com - test_schema


from django.test import TestCase

import app_home.cache as cache
from app_data.data import Instance
from app_data.models import DataClass, DataSchema
from app_data.schema import Schema
from tests import helper


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

    # ---------------------------

    def test_create_schema(self):
        schema = helper.add_schema(
            classname="unittestschema",
            displayname="Unittest Schema",
            options={"icon": "testicon", "unittest_option": "edit"},
            verbose=False,
        )

        self.assertIsNotNone(schema)
        self.assertEqual(schema.displayname, "Unittest Schema")
        self.assertEqual(schema.icon, "testicon")
        self.assertNotIn("unittest_option", schema.options)

    # ---------------------------

    def test_listsall_schema(self):
        helper.add_schema(
            classname="unittestschema",
            displayname="Unittest Schema",
            options={"unittest_option": "edit", "icon": "testicon"},
            verbose=False,
        )

        schemas = Schema.listall()
        # for s in schemas:
        #     print(s)
        self.assertIs(len(schemas), 17)

    # ---------------------------

    def test_delete_schema(self):
        # Create a schema to delete
        schema = helper.add_schema(
            classname="deleteschema", displayname="Delete Test Schema", verbose=False
        )

        self.assertIsNotNone(schema)
        self.assertEqual(schema.displayname, "Delete Test Schema")

        # Verify schema exists
        schema_check = Schema.from_name("deleteschema")
        self.assertIsNotNone(schema_check)

        # Delete the schema using add_schema with action='delete'
        helper.add_schema(classname="deleteschema", action="delete", verbose=False)

        # Verify schema is deleted (should return None)
        deleted_schema = Schema.from_name("deleteschema")
        self.assertIsNone(deleted_schema)

    # ---------------------------

    def test_schema_delete_classmethod(self):
        # Create a schema with 2 fields
        schema = helper.add_schema(
            classname="deletefieldsschema",
            displayname="Delete Fields Schema",
            field_definition={
                "field1": {"dataformat": "string", "displayname": "Field 1"},
                "field2": {"dataformat": "int", "displayname": "Field 2"},
            },
            verbose=False,
        )

        self.assertIsNotNone(schema)
        self.assertEqual(len(schema.fields), 2)

        # Verify DataClass and DataSchema entries exist
        dataclass = DataClass.objects.filter(keyname="deletefieldsschema").first()
        self.assertIsNotNone(dataclass)

        dataschema_count = DataSchema.objects.filter(classname="deletefieldsschema").count()
        self.assertEqual(dataschema_count, 2)

        # Delete the schema using Schema.delete() classmethod
        Schema.delete("deletefieldsschema")

        # Verify schema is deleted (should return None)
        deleted_schema = Schema.from_name("deletefieldsschema")
        self.assertIsNone(deleted_schema)

        # Verify DataClass entry is deleted
        dataclass_after = DataClass.objects.filter(keyname="deletefieldsschema").first()
        self.assertIsNone(dataclass_after)

        # Verify DataSchema entries are deleted
        dataschema_count_after = DataSchema.objects.filter(classname="deletefieldsschema").count()
        self.assertEqual(dataschema_count_after, 0)

    # ---------------------------

    def test_schema_exists_classmethod(self):
        # Test that exists() returns False for non-existent schema
        self.assertFalse(Schema.exists("nonexistentschema"))
        self.assertFalse(Schema.exists(None))

        # Create a schema
        schema = helper.add_schema(
            classname="existsschema", displayname="Exists Test Schema", verbose=False
        )

        self.assertIsNotNone(schema)

        # Test that exists() returns True for existing schema
        self.assertTrue(Schema.exists("existsschema"))

        # Delete the schema
        Schema.delete("existsschema")

        # Test that exists() returns False after deletion
        self.assertFalse(Schema.exists("existsschema"))

    # ---------------------------

    def test_schema_disable_classmethod(self):
        # Create a schema with 2 fields
        schema = helper.add_schema(
            classname="disableschema",
            displayname="Disable Test Schema",
            field_definition={
                "field1": {"dataformat": "string", "displayname": "Field 1"},
                "field2": {"dataformat": "int", "displayname": "Field 2"},
            },
            verbose=False,
        )

        self.assertIsNotNone(schema)
        self.assertEqual(len(schema.fields), 2)

        # Verify schema is enabled initially
        schema_before = Schema.from_name("disableschema")
        self.assertIsNotNone(schema_before)
        dataclass_before = DataClass.objects.filter(keyname="disableschema").first()
        self.assertIsNotNone(dataclass_before)
        self.assertTrue(dataclass_before.is_enabled)

        # Disable the schema using Schema.disable() classmethod
        Schema.disable("disableschema")

        # Verify schema still exists
        schema_after = Schema.from_name("disableschema")
        self.assertIsNotNone(schema_after)

        # Verify schema is disabled in the database
        dataclass_after = DataClass.objects.filter(keyname="disableschema").first()
        self.assertIsNotNone(dataclass_after)
        self.assertFalse(dataclass_after.is_enabled)

    # ---------------------------

    def test_update_schema(self):
        # Create a schema with 2 fields
        schema = helper.add_schema(
            classname="updateschema",
            displayname="Update Test Schema",
            field_definition={
                "name": {"dataformat": "string", "displayname": "Name"},
                "email": {"dataformat": "string", "displayname": "Email"},
            },
            verbose=False,
        )

        self.assertIsNotNone(schema)
        self.assertEqual(schema.displayname, "Update Test Schema")
        self.assertEqual(len(schema.fields), 2)
        self.assertIn("name", schema.fields)
        self.assertIn("email", schema.fields)

        # Verify schema exists with fields
        schema_check = Schema.from_name("updateschema")
        self.assertIsNotNone(schema_check)
        self.assertEqual(len(schema_check.fields), 2)

        # Add a field to the schema
        helper.add_schema_field(
            classname="updateschema",
            fields={"phone": {"dataformat": "string", "displayname": "Phone Number"}},
            verbose=False,
        )

        # Verify schema now has 3 fields
        schema_updated = Schema.from_name("updateschema")
        self.assertEqual(len(schema_updated.fields), 3)
        self.assertIn("name", schema_updated.fields)
        self.assertIn("email", schema_updated.fields)
        self.assertIn("phone", schema_updated.fields)
        self.assertEqual(schema_updated.fields["phone"]["displayname"], "Phone Number")

    # ---------------------------

    def test_create_schema_with_fields(self):

        schema = helper.add_schema(
            classname="schemawithfields",
            displayname="Schema With Fields",
            field_definition={
                "name": {"dataformat": "string", "displayname": "Name"},
                "age": {"dataformat": "int", "displayname": "Age", "description": "Person age"},
                "email": {"dataformat": "string", "displayname": "Email"},
            },
            verbose=False,
        )

        self.assertIsNotNone(schema)
        self.assertEqual(schema.displayname, "Schema With Fields")
        self.assertEqual(len(schema.fields), 3)
        self.assertIn("name", schema.fields)
        self.assertIn("age", schema.fields)
        self.assertIn("email", schema.fields)
        self.assertEqual(schema.fields["name"]["dataformat"], "string")
        self.assertEqual(schema.fields["age"]["dataformat"], "int")
        self.assertEqual(schema.fields["age"]["displayname"], "Age")

    # ---------------------------

    def test_add_schema_field_helper(self):

        schema = helper.add_schema(
            classname="fieldschema", displayname="Field Schema", verbose=False
        )

        self.assertIsNotNone(schema)
        self.assertEqual(len(schema.fields), 0)

        helper.add_schema_field(
            classname="fieldschema",
            fields={
                "name": {"dataformat": "string", "displayname": "Name"},
                "count": {
                    "dataformat": "int",
                    "displayname": "Count",
                    "description": "Number of items",
                },
            },
            verbose=False,
        )

        # Verify that the schema still exists and is accessible
        schema = Schema.from_name("fieldschema")
        self.assertIsNotNone(schema)
        self.assertEqual(schema.classname, "fieldschema")
        self.assertEqual(schema.displayname, "Field Schema")
        self.assertEqual(len(schema.fields), 2)

    # ---------------------------
    # instance
    # ---------------------------

    def test_empty_instance(self):
        i = Instance()
        self.assertIsNotNone(i)

    # ---------------------------

    def test_instance(self):
        # Create a schema with 2 fields
        schema = helper.add_schema(
            classname="instanceschema",
            displayname="Instance Test Schema",
            field_definition={
                "name": {"dataformat": "string", "displayname": "Name"},
                "email": {"dataformat": "string", "displayname": "Email"},
            },
            verbose=False,
        )

        self.assertIsNotNone(schema)
        self.assertEqual(len(schema.fields), 2)

        # Create an instance with field values
        helper.add_instance(
            classname="instanceschema",
            keyname="instance01",
            fields={"name": "John Doe", "email": "john@example.com"},
            verbose=False,
        )

        # Get the instance and check field values
        instance = Instance.from_keyname(classname="instanceschema", keyname="instance01")
        self.assertIsNotNone(instance)
        self.assertIn("name", instance.fields)
        self.assertEqual(instance.fields["name"].get_first_value(), "John Doe")
        self.assertEqual(instance.fields["email"].get_first_value(), "john@example.com")

    # ---------------------------
    # Static methods
    # ---------------------------

    def test_get_empty_field_dict(self):
        field_dict = Schema.get_empty_field_dict()

        # Verify the returned dict has all required keys and correct defaults
        self.assertIsInstance(field_dict, dict)
        self.assertEqual(field_dict["displayname"], "")
        self.assertTrue(field_dict["is_enabled"])
        self.assertEqual(field_dict["dataformat"], "string")
        self.assertEqual(field_dict["dataformat_ext"], "")
        self.assertEqual(field_dict["order"], 100)
        self.assertEqual(field_dict["page"], "Default")
        self.assertEqual(field_dict["cardinal_min"], 0)
        self.assertEqual(field_dict["cardinal_max"], 1)
        self.assertEqual(field_dict["default_value"], "")

    def test_update_field_from_dict(self):
        # Create initial field dict with all fields
        field_dict = {
            "displayname": "Original Name",
            "is_enabled": True,
            "dataformat": "string",
            "dataformat_ext": "",
            "order": 10,
            "page": "Default",
            "cardinal_min": 0,
            "cardinal_max": 1,
            "default_value": "",
        }

        # Test 1: No changes - should return False
        update_dict = {
            "displayname": "Original Name",
            "dataformat": "string",
        }
        changed = Schema.update_field_from_dict(field_dict, update_dict)
        self.assertFalse(changed)
        self.assertEqual(field_dict["displayname"], "Original Name")
        self.assertEqual(field_dict["dataformat"], "string")
        self.assertEqual(field_dict["order"], 10)

        # Test 2: Update multiple fields - should return True
        update_dict = {
            "displayname": "Updated Name",
            "order": 20,
            "page": "CustomPage",
            "cardinal_max": 5,
        }
        changed = Schema.update_field_from_dict(field_dict, update_dict)
        self.assertTrue(changed)
        self.assertEqual(field_dict["displayname"], "Updated Name")
        self.assertEqual(field_dict["order"], 20)
        self.assertEqual(field_dict["page"], "CustomPage")
        self.assertEqual(field_dict["cardinal_max"], 5)
        # Verify unchanged fields stay the same
        self.assertTrue(field_dict["is_enabled"])
        self.assertEqual(field_dict["dataformat"], "string")
        self.assertEqual(field_dict["cardinal_min"], 0)
        self.assertEqual(field_dict["default_value"], "")

    # ---------------------------

    def test_invalid_dataformat_stored_as_unknown(self):
        helper.add_schema(
            classname="schema_invalid_fmt",
            field_definition={"field1": {"dataformat": "static", "displayname": "Bad Field"}},
            verbose=False,
        )

        db_field = DataSchema.objects.filter(
            classname="schema_invalid_fmt", keyname="field1"
        ).first()
        self.assertIsNotNone(db_field)
        self.assertEqual(db_field.dataformat, "unknown")

    # ---------------------------

    def test_update_invalid_dataformat_stored_as_unknown(self):
        helper.add_schema(
            classname="schema_update_fmt",
            field_definition={"field1": {"dataformat": "string", "displayname": "Good Field"}},
            verbose=False,
        )

        helper.add_schema_field(
            classname="schema_update_fmt",
            fields={"field1": {"dataformat": "bogus", "displayname": "Updated Field"}},
        )

        db_field = DataSchema.objects.filter(
            classname="schema_update_fmt", keyname="field1"
        ).first()
        self.assertIsNotNone(db_field)
        self.assertEqual(db_field.dataformat, "unknown")

    # ---------------------------
    # keyname_label
    # ---------------------------

    # ---------------------------
    # displayname_label
    # ---------------------------
