"""Protected module containing ECM object type definition dataclasses."""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum

from ecmind_blue_client.ecm.definition._ecm_field_definition import ECMFieldDefinition
from ecmind_blue_client.ecm.definition._ecm_description_entry import ECMDescriptionEntry


class ECMMainType(Enum):
    """Numeric main type identifier of an ECM object type.

    Attributes:
        FOLDER: Folder object type (``0``).
        REGISTER: Register object type (``99``).
        DOC_GRAYSCALE: Grayscale image document (``1``).
        DOC_BW: Black-and-white image document (``2``).
        DOC_COLOR: Colour image document (``3``).
        DOC_WINDOWS: Windows application document (``4``).
        DOC_MULTIMEDIA: Multimedia document (``5``).
        DOC_MAIL: E-mail document (``6``).
        DOC_XML: XML document (``7``).
        DOC_CONTAINER: Container document (``8``).
    """

    FOLDER = 0
    REGISTER = 99
    DOC_GRAYSCALE = 1
    DOC_BW = 2
    DOC_COLOR = 3
    DOC_WINDOWS = 4
    DOC_MULTIMEDIA = 5
    DOC_MAIL = 6
    DOC_XML = 7
    DOC_CONTAINER = 8


class ECMIndexHistoryMode(Enum):
    """Controls whether index data history is created for an object type.

    The value combines two bits from the ``historyflags`` bitmask:
    bit 0 (``1``) enables the feature and bit 3 (``8``) restricts it to new objects only.

    Attributes:
        DISABLED: No index data history is created (``0``).
        ENABLED: Index data history is always created (``1``).
        DISABLED_ON_CREATION: Index data history is not created, applies only on new creation (``8``).
        ENABLED_ON_CREATION: Index data history is created, applies only on new creation (``9``).
    """

    DISABLED = 0
    ENABLED = 1
    DISABLED_ON_CREATION = 8
    ENABLED_ON_CREATION = 9


class ECMDocumentHistoryMode(Enum):
    """Controls whether document history is created for an object type.

    The value combines two bits from the ``historyflags`` bitmask:
    bit 1 (``2``) enables the feature and bit 4 (``16``) restricts it to new objects only.

    Attributes:
        DISABLED: No document history is created (``0``).
        ENABLED: Document history is always created (``2``).
        DISABLED_ON_CREATION: Document history is not created, applies only on new creation (``16``).
        ENABLED_ON_CREATION: Document history is created, applies only on new creation (``18``).
    """

    DISABLED = 0
    ENABLED = 2
    DISABLED_ON_CREATION = 16
    ENABLED_ON_CREATION = 18


@dataclass(frozen=True)
class ECMHistoryFlags:
    """Decoded representation of the ``historyflags`` bitmask of an ECM object type.

    Attributes:
        index_history: Controls index data history creation.
        document_history: Controls document history creation.
        document_versions: Whether document versions are created (bit 2).
        page_count: Whether page count is generated (bit 6).
    """

    index_history: ECMIndexHistoryMode
    document_history: ECMDocumentHistoryMode
    document_versions: bool
    page_count: bool

    @classmethod
    def _from_int(cls, value: int) -> ECMHistoryFlags:
        """Decode a raw ``historyflags`` integer into an ``ECMHistoryFlags`` instance.

        Args:
            value: The raw integer value of the ``historyflags`` attribute.

        Returns:
            A populated ``ECMHistoryFlags`` instance.
        """
        return cls(
            index_history=ECMIndexHistoryMode((value & 0x01) | (value & 0x08)),
            document_history=ECMDocumentHistoryMode((value & 0x02) | (value & 0x10)),
            document_versions=bool(value & 0x04),
            page_count=bool(value & 0x40),
        )


class ECMCompressionMode(Enum):
    """File compression mode of an ECM object type.

    Attributes:
        DISABLED: No compression (``0``).
        MERGE_ONLY: Files are merged into a single archive without compression (``1``).
        MERGE_AND_COMPRESS: Files are merged and compressed (``2``).
    """

    DISABLED = 0
    MERGE_ONLY = 1
    MERGE_AND_COMPRESS = 2


class ECMFulltextMode(Enum):
    """Full-text indexing mode of an ECM object type.

    Attributes:
        DISABLED: Full-text indexing is disabled (``0``).
        AUTOMATIC: Full-text indexing is enabled and runs automatically (``1``).
        MANUAL: Full-text indexing is enabled but does not run automatically (``2``).
    """

    DISABLED = 0
    AUTOMATIC = 1
    MANUAL = 2



@dataclass(frozen=True)
class ECMLimitedObjectEntry:
    """A single entry from the ``<limited_object>`` XML element.

    Defines the maximum allowed number of child objects of a specific type that may exist
    below the owning object type.  The raw ``type`` attribute encodes both the main type
    and the cotype as ``(maintype << 16) | cotype``.

    Attributes:
        object_type_id: Raw encoded type identifier (``(maintype << 16) | cotype``).
        count: Maximum allowed number of child objects of this type (``0`` = unlimited).
    """

    object_type_id: int
    count: int

    @property
    def cotype(self) -> int:
        """Object type ID (low 16 bits of ``object_type_id``).

        Returns:
            The ``cotype`` of the referenced object type.
        """
        return self.object_type_id & 0xFFFF

    @property
    def maintype_value(self) -> int:
        """Raw main type value (bits 16–23 of ``object_type_id``).

        Returns:
            The numeric ``maintype`` of the referenced object type.
        """
        return (self.object_type_id >> 16) & 0xFF


@dataclass(frozen=True)
class ECMObjectDefinition:
    """Shared base for all ECM object type definitions parsed from asobjdef XML.

    Attributes:
        maintype: Main type of the object type.
        cotype: Numeric object type ID used in server communication.
        internal_name: Internal (programmatic) name of the object type.
        os_guid: Server-side GUID identifying this object type.
        tablename: Primary database table name.
        ext_tablename: Extended database table name, empty if not used.
        icon_id: Icon identifier for the object type.
        history_flags: Decoded history tracking flags.
        compression_flags: File compression mode.
        reference_type: Reference type identifier.
        ki_search_index: Whether KI search indexing is enabled.
        no_dias: Whether the object type has no DIA slots.
        fulltext: Full-text indexing mode.
        description: Name entry for the primary language, or ``None`` if not available.
        descriptions: All language-specific name entries, keyed by ISO locale code.
        fields: All field definitions belonging to this object type, keyed by internal name.
            Only contains top-level fields; use ``all_fields`` or ``[]`` to include page fields.
        limited_objects: Allowed child object types and their maximum counts, in declaration
            order.  Empty for object types that have no ``<limited_objects>`` element.
        type_id: Composite type identifier ``(maintype.value << 16) | cotype``, matching the
            ``type`` attribute used in ``<limited_object>`` elements.
        frame_left: Left edge of the object's index form bounding box in form grid units.
        frame_top: Top edge of the object's index form bounding box in form grid units.
        frame_right: Right edge of the object's index form bounding box in form grid units.
        frame_bottom: Bottom edge of the object's index form bounding box in form grid units.
        multiframe_width: Width of the multi-frame layout area in form grid units (``0`` = not configured).
        multiframe_height: Height of the multi-frame layout area in form grid units (``0`` = not configured).
    """

    maintype: ECMMainType
    cotype: int
    internal_name: str
    os_guid: str
    tablename: str
    ext_tablename: str
    icon_id: int
    history_flags: ECMHistoryFlags
    compression_flags: ECMCompressionMode
    reference_type: int
    ki_search_index: bool
    no_dias: bool
    fulltext: ECMFulltextMode
    description: ECMDescriptionEntry
    descriptions: dict[str, ECMDescriptionEntry]
    fields: dict[str, ECMFieldDefinition]
    limited_objects: list[ECMLimitedObjectEntry] = field(default_factory=list)
    frame_left: int = 0
    frame_top: int = 0
    frame_right: int = 0
    frame_bottom: int = 0
    multiframe_width: int = 0
    multiframe_height: int = 0

    @property
    def type_id(self) -> int:
        """Composite type identifier matching the ``type`` attribute in ``<limited_object>`` elements.

        Returns:
            ``(maintype.value << 16) | cotype``
        """
        return (self.maintype.value << 16) | self.cotype

    @staticmethod
    def _collect_fields(fields: "dict[str, ECMFieldDefinition]") -> "dict[str, ECMFieldDefinition]":
        """Recursively collect all fields, including those nested inside page controls.

        Args:
            fields: The top-level or page-level field dict to start from.

        Returns:
            A flat dict of all fields keyed by internal name.
        """
        result: dict[str, ECMFieldDefinition] = {}
        for name, f in fields.items():
            result[name] = f
            for page in f.pages.values():
                result.update(ECMObjectDefinition._collect_fields(page.fields))
        return result

    @property
    def all_fields(self) -> "dict[str, ECMFieldDefinition]":
        """All field definitions flattened across all levels, keyed by internal name.

        Recursively traverses PageControl pages so that every field — regardless of
        nesting depth — is accessible in a single flat dict.

        Returns:
            A flat dict of all fields keyed by internal name.
        """
        return self._collect_fields(self.fields)

    def __getitem__(self, internal_name: str) -> "ECMFieldDefinition":
        """Return the field with the given internal name from any nesting level.

        Args:
            internal_name: The internal name of the field to look up.

        Returns:
            The matching ``ECMFieldDefinition``.

        Raises:
            KeyError: If no field with ``internal_name`` exists on this object type.
        """
        return self.all_fields[internal_name]


@dataclass(frozen=True)
class ECMFolderDefinition(ECMObjectDefinition):
    """Folder object type definition (``maintype=FOLDER``)."""


@dataclass(frozen=True)
class ECMRegisterDefinition(ECMObjectDefinition):
    """Register object type definition (``maintype=REGISTER``)."""


@dataclass(frozen=True)
class ECMDocumentDefinition(ECMObjectDefinition):
    """Document object type definition (all document ``maintype`` values).

    Attributes:
        cross_module_document_type: Whether this document type spans multiple modules
            (moduleübergreifender Dokumenttyp).
    """

    cross_module_document_type: bool = False
