import unittest

from ecmind_blue_client.ecm.definition import (
    ECMCatalogType,
    ECMCompressionMode,
    ECMControlType,
    ECMDefaultValueMode,
    ECMDefinition,
    ECMDocumentDefinition,
    ECMDocumentHistoryMode,
    ECMFieldDataType,
    ECMFieldList,
    ECMFolderDefinition,
    ECMFulltextMode,
    ECMIndexHistoryMode,
    ECMLimitedObjectEntry,
    ECMListNodeEntry,
    ECMListRowEntry,
    ECMMainType,
    ECMRegisterDefinition,
    ECMTableColumnDefinition,
    ECMTextAlignment,
)


class TestDefinition(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        with open("./tests/testdata/asobjdef.xml", "r", encoding="UTF-16") as f:
            cls.definition = ECMDefinition(f.read())
        cls.unittest_obj = cls.definition.cabinets["Unittest"]["Unittest"]
        cls.def_obj = cls.definition.cabinets["DefinitionTests"]["DefinitionTests"]
        cls.tab_input = cls.def_obj.fields["PageControl"].pages["TabInputOptions"]

    def test_definition_metadata(self):
        self.assertEqual(self.definition.limited_objects, True)
        self.assertEqual(self.definition.version, "5.20")
        self.assertEqual(self.definition.created.isoformat(), "2026-03-11T17:50:07")

    def test_languages(self):
        # Primary language
        self.assertEqual(self.definition.primary_language.lang_id, 7)
        self.assertEqual(self.definition.primary_language.iso_code, "de_DE")
        self.assertEqual(self.definition.primary_language.name, "German")
        self.assertTrue(self.definition.primary_language.is_primary)
        self.assertTrue(self.definition.primary_language.is_active)

        # available_languages only contains active languages
        self.assertEqual(self.definition.available_languages["de_DE"].lang_id, 7)
        with self.assertRaises(KeyError):
            _ = self.definition.available_languages["it_IT"]

        # all_languages includes inactive ones
        self.assertEqual(self.definition.all_languages["it_IT"].lang_id, 16)
        self.assertFalse(self.definition.all_languages["it_IT"].is_active)

    def test_cabinet_structure(self):
        self.assertEqual(len(self.definition.cabinets), 3)

        unittest_cabinet = self.definition.cabinets["Unittest"]
        self.assertEqual(unittest_cabinet.cotype, 0)

        unitdms_cabinet = self.definition.cabinets["Unittest_DMS"]
        self.assertEqual(unitdms_cabinet.cotype, 1)

        self.assertEqual(unitdms_cabinet.objects["Unittest_DMS"], unitdms_cabinet.folders["Unittest_DMS"])
        self.assertEqual(unitdms_cabinet.objects["Unittest_DMS_Subregister"], unitdms_cabinet.registers["Unittest_DMS_Subregister"])
        self.assertEqual(unitdms_cabinet.objects["Unittest_DMS_Document"], unitdms_cabinet.documents["Unittest_DMS_Document"])
        self.assertEqual(self.definition["Unittest_DMS_Document"], unitdms_cabinet.documents["Unittest_DMS_Document"])

    def test_cabinet_dict_api(self):
        cab = self.definition.cabinets["Unittest"]

        self.assertIn("Unittest", cab)
        self.assertNotIn("DoesNotExist", cab)
        self.assertEqual(len(cab), 4)
        self.assertIn("Unittest", list(cab))
        self.assertIn("Unittest", cab.keys())
        self.assertIn(cab["Unittest"], cab.values())
        self.assertIn(("Unittest", cab["Unittest"]), list(cab.items()))

    def test_object_type_instances(self):
        unittest_cab = self.definition.cabinets["Unittest"]
        self.assertIsInstance(unittest_cab["Unittest"], ECMFolderDefinition)
        self.assertIsInstance(unittest_cab["UnittestReg"], ECMRegisterDefinition)
        self.assertIsInstance(unittest_cab["UnittestDoc"], ECMDocumentDefinition)

    def test_object_properties(self):
        doc = self.definition.cabinets["Unittest_DMS"].documents["Unittest_DMS_Document"]

        self.assertEqual(doc.internal_name, "Unittest_DMS_Document")
        self.assertEqual(doc.compression_flags, ECMCompressionMode.DISABLED)
        self.assertEqual(doc.maintype, ECMMainType.DOC_WINDOWS)
        self.assertEqual(doc.cotype, 1)
        self.assertEqual(doc.type_id, (4 << 16) | 1)  # maintype=DOC_WINDOWS(4), cotype=1
        self.assertEqual(doc.fulltext, ECMFulltextMode.AUTOMATIC)
        self.assertTrue(doc.cross_module_document_type)
        self.assertEqual(doc.icon_id, 1073744046)
        self.assertFalse(doc.ki_search_index)
        self.assertEqual(doc.os_guid, "8A6E3E8CDE6D4F4B90D05399E032EB20")
        self.assertEqual(doc.tablename, "object2")

        self.assertEqual(doc.history_flags.document_history, ECMDocumentHistoryMode.DISABLED)
        self.assertEqual(doc.history_flags.index_history, ECMIndexHistoryMode.DISABLED)
        self.assertFalse(doc.history_flags.page_count)
        self.assertFalse(doc.history_flags.document_versions)

        self.assertEqual(doc.frame_left, 0)
        self.assertEqual(doc.frame_top, 0)
        self.assertEqual(doc.frame_right, 300)
        self.assertEqual(doc.frame_bottom, 306)
        self.assertEqual(doc.multiframe_width, 0)
        self.assertEqual(doc.multiframe_height, 0)

        self.assertEqual(doc.description.name, "Dokument")
        self.assertEqual(doc.description.tooltip, "")

    def test_object_extended_properties(self):
        obj = self.def_obj
        self.assertEqual(obj.ext_tablename, "")
        self.assertEqual(obj.reference_type, 0)
        self.assertFalse(obj.ki_search_index)
        self.assertTrue(obj.no_dias)
        self.assertEqual(obj.fulltext, ECMFulltextMode.DISABLED)
        self.assertEqual(obj.descriptions["de_DE"].name, "DefinitionTests")

    def test_field_positions_and_access(self):
        doc = self.definition.cabinets["Unittest_DMS"].documents["Unittest_DMS_Document"]

        name_field = doc.fields["Name"]
        self.assertEqual(name_field.os_guid, "FE49BE50330C47E6BE385A33513CB547")
        self.assertEqual(name_field.description.name, "Name")
        self.assertEqual(name_field.label_position.left, 5)
        self.assertEqual(name_field.label_position.top, 7)
        self.assertEqual(name_field.label_position.right, 70)
        self.assertEqual(name_field.label_position.bottom, 9)
        self.assertEqual(name_field.control_position.left, 80)
        self.assertEqual(name_field.control_position.top, 5)
        self.assertEqual(name_field.control_position.right, 200)
        self.assertEqual(name_field.control_position.bottom, 12)

        # all_fields flattens page fields; __getitem__ is a shortcut for all_fields
        self.assertEqual(
            self.def_obj.all_fields["InputRequired"],
            self.def_obj.fields["PageControl"].pages["TabInputOptions"].fields["InputRequired"],
        )
        self.assertEqual(self.def_obj.all_fields["InputRequired"], self.def_obj["InputRequired"])

    def test_field_basic_properties(self):
        f = self.tab_input.fields["InputRequired"]

        self.assertEqual(f.tab_order, 0)
        self.assertEqual(f.internal_name, "InputRequired")
        self.assertEqual(f.field_name, "feld12")
        self.assertEqual(f.os_guid, "87A9E82C815C4E4CB7169EEB7460F522")
        self.assertEqual(f.input_length, 50)
        self.assertEqual(f.case, 1)
        self.assertEqual(f.regular_expression, "")
        self.assertEqual(f.search_group_name, "")
        self.assertEqual(f.prn_alias, "")
        self.assertFalse(f.is_static)

        self.assertEqual(self.tab_input.fields["FieldWithRegex"].regular_expression, ".*")
        self.assertEqual(self.tab_input.fields["Searchgroup"].search_group_name, "Suchgruppe1")
        self.assertEqual(self.tab_input.fields["MaskProtected"].prn_alias, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")

        self.assertIsNone(self.unittest_obj.fields["GroupField"].input_length)
        self.assertTrue(self.unittest_obj.fields["GroupField"].is_static)

    def test_field_data_types(self):
        def_tests = self.definition.cabinets["DefinitionTests"]

        self.assertEqual(def_tests["DefinitionTests"]["AllCharacters"].data_type, ECMFieldDataType.STRING)
        self.assertEqual(def_tests["DefinitionTests"]["Date"].data_type, ECMFieldDataType.DATE)
        self.assertEqual(def_tests["DefinitionTests"]["DateTime"].data_type, ECMFieldDataType.DATE_TIME)
        self.assertEqual(def_tests["DefinitionTests"]["Time"].data_type, ECMFieldDataType.TIME)
        self.assertEqual(def_tests["DefinitionTests"]["float"].data_type, ECMFieldDataType.DOUBLE)
        self.assertEqual(def_tests["DefinitionTests"]["Ziffern"].data_type, ECMFieldDataType.INTEGER)
        self.assertEqual(self.unittest_obj.fields["CheckboxField"].data_type, ECMFieldDataType.BOOLEAN)
        self.assertIsNone(self.def_obj.fields["PageControl"].pages["TabLabel"].fields["SimpleLabelLeft"].data_type)

    def test_field_input_flags(self):
        fields = self.tab_input.fields

        self.assertTrue(fields["InputRequired"].required)
        self.assertFalse(fields["InputSupervisor"].required)

        self.assertTrue(fields["InputSupervisor"].supervisor)
        self.assertFalse(fields["InputRequired"].supervisor)

        self.assertTrue(fields["ReadOnly"].readonly)
        self.assertFalse(fields["ReadOnlyAfterArchiving"].readonly)

        self.assertTrue(fields["ReadOnlyAfterArchiving"].readonly_after_archiving)
        self.assertFalse(fields["ReadOnly"].readonly_after_archiving)

        self.assertTrue(fields["ReadOnlyAfterIntitializing"].readonly_after_initialization)
        self.assertFalse(fields["ReadOnly"].readonly_after_initialization)

        self.assertTrue(fields["KeyField"].key_field)
        self.assertFalse(fields["InputRequired"].key_field)

        self.assertTrue(fields["WithLeadingZeros"].leading_zeros)
        self.assertFalse(fields["InputRequired"].leading_zeros)

        self.assertTrue(fields["Multiline"].multiline)
        self.assertFalse(fields["InputRequired"].multiline)

    def test_field_visibility_flags(self):
        fields = self.tab_input.fields

        self.assertFalse(fields["LabelHidden"].label_visible)
        self.assertTrue(fields["InputRequired"].label_visible)

        self.assertTrue(fields["ControlHidden"].control_hidden)
        self.assertFalse(fields["InputRequired"].control_hidden)

    def test_field_protection_flags(self):
        fields = self.tab_input.fields

        self.assertTrue(fields["FieldWithRegex"].regex_protected)
        self.assertFalse(fields["IgnoreCase"].regex_protected)

        self.assertTrue(fields["MaskProtected"].mask_protected)
        self.assertFalse(fields["InputRequired"].mask_protected)

    def test_field_crosscheck(self):
        fields = self.tab_input.fields

        self.assertEqual(fields["KontrolledByCrosscheck"].crosscheck_position, 3)
        self.assertEqual(fields["KontrolledByCrosscheck"].crosscheck_target_tab_position, 2)
        self.assertEqual(fields["InputRequired"].crosscheck_position, 0)
        self.assertEqual(fields["InputRequired"].crosscheck_target_tab_position, 0)

    def test_field_default_value(self):
        fields = self.tab_input.fields

        self.assertEqual(fields["ValuePredefinedCurrentID"].default_value_mode, ECMDefaultValueMode.FUNCTION)
        self.assertEqual(fields["ValuePredefinedCurrentID"].default_value_function, 6)
        self.assertEqual(fields["ValuePredefinedConstant"].default_value_mode, ECMDefaultValueMode.CONSTANT)
        self.assertEqual(fields["ValuePredefinedConstant"].default_value_function, 0)
        self.assertIsNone(fields["IgnoreCase"].default_value_mode)

    def test_field_appearance(self):
        fields = self.tab_input.fields

        self.assertEqual(fields["TextColor"].textcolor, (128, 128, 255))
        self.assertEqual(fields["ReadOnly"].textcolor, (0, 0, 0))

        self.assertEqual(self.def_obj["SimpleLabelLeft"].text_alignment, ECMTextAlignment.LEFT)
        self.assertEqual(self.def_obj["SimpleLabelCenter"].text_alignment, ECMTextAlignment.CENTER)
        self.assertEqual(self.def_obj["SimpleLabelRight"].text_alignment, ECMTextAlignment.RIGHT)
        self.assertEqual(fields["ReadOnly"].text_alignment, ECMTextAlignment.RIGHT)

    def test_field_catalog_types(self):
        fields = self.tab_input.fields

        self.assertEqual(fields["CatalogDatabase"].catalog, ECMCatalogType.DATABASE)
        self.assertEqual(fields["CatalogList"].catalog, ECMCatalogType.LIST)
        self.assertEqual(fields["CatalogTreeList"].catalog, ECMCatalogType.TREE)
        self.assertEqual(fields["CatalogHierachyTree"].catalog, ECMCatalogType.HIERARCHY_TREE)
        self.assertEqual(fields["CatalogStructuretree"].catalog, ECMCatalogType.STRUCTURE_TREE)
        self.assertEqual(fields["CatalogAddon"].catalog, ECMCatalogType.ADDON)
        self.assertIsNone(fields["InputRequired"].catalog)

        self.assertTrue(fields["CatalogAddonAutoExecute"].addon_auto_execute)
        self.assertFalse(fields["CatalogAddon"].addon_auto_execute)

    def test_field_control_types(self):
        pages = self.def_obj.fields["PageControl"].pages

        self.assertEqual(self.tab_input.fields["InputRequired"].control, ECMControlType.TEXTFIELD)
        self.assertEqual(self.unittest_obj.fields["TableField"].control, ECMControlType.TABLEFIELD)
        self.assertEqual(self.unittest_obj.fields["RadioField1"].control, ECMControlType.RADIOBUTTON)
        self.assertEqual(self.unittest_obj.fields["CheckboxField"].control, ECMControlType.CHECKBOX)
        self.assertEqual(pages["TabImages"].fields["EcmindImage"].control, ECMControlType.PICTURE)
        self.assertEqual(self.unittest_obj.fields["GroupField"].control, ECMControlType.GROUP)
        self.assertEqual(self.def_obj.fields["PageControl"].control, ECMControlType.PAGE_CONTROL)
        self.assertEqual(pages["TabBrowser"].fields["WebControl"].control, ECMControlType.WEBCONTROL)
        self.assertEqual(pages["TabButtons"].fields["Button1"].control, ECMControlType.BUTTON)
        self.assertEqual(pages["TabLabel"].fields["SimpleLabelLeft"].control, ECMControlType.LABEL)

    def test_table_column_definition(self):
        table_field = self.unittest_obj.fields["TableField"]

        self.assertEqual(len(table_field.columns), 4)
        self.assertIn("TableCol1", table_field.columns)

        col = table_field.columns["TableCol1"]
        self.assertIsInstance(col, ECMTableColumnDefinition)
        self.assertEqual(col.internal_name, "TableCol1")
        self.assertEqual(col.os_guid, "A034418E672A4DC9B68373601DC03049")
        self.assertEqual(col.field_name, "feld1")
        self.assertEqual(col.data_type, ECMFieldDataType.STRING)
        self.assertEqual(col.id, 0)
        self.assertEqual(col.length, 50)
        self.assertEqual(col.col_width, 0)
        self.assertEqual(col.col_pos, 0)
        self.assertEqual(col.color, 0)
        self.assertEqual(col.text_align, 0)
        self.assertEqual(col.catalog_type, 0)
        self.assertEqual(col.link_id, 0)
        self.assertFalse(col.readonly)
        self.assertEqual(col.values_id, "")
        self.assertEqual(col.descriptions["de_DE"].name, "Table Col 1")

        # column order via col_pos
        self.assertEqual(table_field.columns["TableCol2"].col_pos, 1)
        self.assertEqual(table_field.columns["TableCol2"].data_type, ECMFieldDataType.INTEGER)
        self.assertEqual(table_field.columns["TableCol3"].col_pos, 2)
        self.assertEqual(table_field.columns["TableCol3"].data_type, ECMFieldDataType.DATE)
        self.assertEqual(table_field.columns["TableCol4"].col_pos, 3)
        self.assertEqual(table_field.columns["TableCol4"].data_type, ECMFieldDataType.DOUBLE)

        # fields without listctrl have empty columns dict
        self.assertEqual(len(self.unittest_obj.fields["StringField"].columns), 0)

    def test_page_definition(self):
        page_ctrl = self.def_obj.fields["PageControl"]

        self.assertIn("TabLabel", page_ctrl.pages)
        self.assertIn("TabInputOptions", page_ctrl.pages)

        tab_label = page_ctrl.pages["TabLabel"]
        self.assertEqual(tab_label.page_id, "1CDC278EBA8D48519A5AD63DCF7B2AAB")
        self.assertEqual(tab_label.position, 0)
        self.assertIn("de_DE", tab_label.descriptions)
        self.assertEqual(tab_label.descriptions["de_DE"].name, "Labels")
        self.assertIn("SimpleLabelLeft", tab_label.fields)

        tab_input = page_ctrl.pages["TabInputOptions"]
        self.assertEqual(tab_input.page_id, "AD6E29208A4D4C94B62BBE55EA9C47D4")
        self.assertEqual(tab_input.position, 1)
        self.assertEqual(tab_input.descriptions["de_DE"].name, "InputOptions")

    def test_field_list_values(self):
        unittest_doc = self.definition.cabinets["Unittest"].documents["UnittestDoc"]

        # flat list
        flat = unittest_doc.fields["ListAddonField"].list_values
        assert isinstance(flat, ECMFieldList)
        self.assertFalse(flat.multiselection)
        self.assertEqual(flat.separator, "")
        self.assertEqual(len(flat.rows), 4)
        assert isinstance(flat.rows[0], ECMListRowEntry)
        self.assertEqual(flat.rows[0].index, 0)
        self.assertEqual(flat.rows[0].value, "Dog")
        self.assertEqual(flat.rows[3].value, "000")
        self.assertEqual(flat.nodes, [])

        # tree list
        tree = unittest_doc.fields["TreeAddonField"].list_values
        assert isinstance(tree, ECMFieldList)
        self.assertEqual(tree.separator, "|")
        self.assertEqual(tree.rows, [])
        self.assertEqual(len(tree.nodes), 2)
        assert isinstance(tree.nodes[0], ECMListNodeEntry)
        self.assertEqual(tree.nodes[0].index, 0)
        self.assertEqual(tree.nodes[0].name, "Animals")
        self.assertEqual(tree.nodes[0].value, "Animals")
        self.assertEqual(len(tree.nodes[0].children), 2)
        self.assertEqual(tree.nodes[0].children[0].name, "Mammals")
        self.assertEqual(len(tree.nodes[0].children[0].children), 2)
        self.assertEqual(tree.nodes[0].children[0].children[0].name, "Human")
        self.assertEqual(tree.nodes[0].children[0].children[0].children, [])

        # tree-specific sub-elements
        self.assertFalse(tree.use_intermediate_nodes)
        self.assertEqual(tree.match_mode, 0)
        self.assertEqual(tree.width, 0)
        self.assertEqual(tree.height, 0)
        self.assertEqual(tree.addon32, "")
        self.assertIn("ZEILE0=Animals", tree.rawdata)

        # flat list has rawdata but no tree-specific fields
        self.assertFalse(flat.use_intermediate_nodes)
        self.assertIn("ZEILE0=Dog", flat.rawdata)

        # add-on catalog carries addon32
        tab_input_fields = self.tab_input.fields
        addon = tab_input_fields["CatalogAddon"].list_values
        assert isinstance(addon, ECMFieldList)
        self.assertEqual(addon.addon32, "axaddate.dll")
        self.assertEqual(addon.rows, [])
        self.assertEqual(addon.nodes, [])

        # field without <list> element
        self.assertIsNone(self.unittest_obj.fields["StringField"].list_values)

    def test_limited_objects(self):
        dms_cab = self.definition.cabinets["Unittest_DMS"]
        folder = dms_cab.folders["Unittest_DMS"]
        register = dms_cab.registers["Unittest_DMS_Register"]
        subregister = dms_cab.registers["Unittest_DMS_Subregister"]

        # folder allows max 10 registers, unlimited subregisters and documents
        self.assertEqual(len(folder.limited_objects), 3)
        self.assertIsInstance(folder.limited_objects[0], ECMLimitedObjectEntry)
        self.assertEqual(folder.limited_objects[0].cotype, 2)
        self.assertEqual(folder.limited_objects[0].maintype_value, 99)
        self.assertEqual(folder.limited_objects[0].count, 10)
        self.assertEqual(folder.limited_objects[1].cotype, 3)
        self.assertEqual(folder.limited_objects[1].count, 0)
        self.assertEqual(folder.limited_objects[2].cotype, 1)
        self.assertEqual(folder.limited_objects[2].maintype_value, 4)

        # register allows max 15 subregisters
        self.assertEqual(register.limited_objects[1].cotype, 3)
        self.assertEqual(register.limited_objects[1].count, 15)

        # subregister allows max 1000 documents
        self.assertEqual(subregister.limited_objects[2].cotype, 1)
        self.assertEqual(subregister.limited_objects[2].count, 1000)

        # object without limited_objects element has empty list
        self.assertEqual(self.unittest_obj.limited_objects, [])
