"""Abstract base for query elements that can hold a condition group or a condition object."""
import xml.etree.ElementTree as ET
from abc import ABC
from datetime import date, datetime

from ecmind_blue_client.const import SystemFields
from ecmind_blue_client.ecm.builder._common import (
    DmsQueryConditionFieldOperatorsT,
    DmsQueryConditionGroupOperatorsT,
    DmsQueryFieldValueT,
    DmsQuerySpecialValue,
    write_field_value,
)
from ecmind_blue_client.ecm.builder._dms_query_table_condition import (
    DmsQueryTableCondition,
)

DmsQueryUserValueT = str | DmsQuerySpecialValue
"""Type alias for user-value arguments: a plain username string or a ``DmsQuerySpecialValue``."""


def _write_user_value(parent: ET.Element, value: DmsQueryUserValueT):
    """Appends a ``<Value>`` child to ``parent`` for a user value.

    Sets ``special="1"`` for ``DmsQuerySpecialValue`` members; writes plain text otherwise.
    """
    if isinstance(value, DmsQuerySpecialValue):
        ET.SubElement(parent, "Value", {"special": "1"}).text = value.value
    else:
        ET.SubElement(parent, "Value").text = value


def _date_to_str(value: date | datetime | str) -> str:
    """Formats a date/datetime value for use in XML range filters. Strings are returned unchanged.

    - ``datetime`` → ``DD.MM.YYYY HH:MM:SS``
    - ``date`` → ``DD.MM.YYYY``
    - ``str`` → unchanged
    """
    if isinstance(value, datetime):  # must precede date check — datetime is a subclass of date
        return value.strftime("%d.%m.%Y %H:%M:%S")
    if isinstance(value, date):
        return value.strftime("%d.%m.%Y")
    return value


class DmsQueryObjectConditionGroupOrConditionObject(ABC):
    """
    This class represents a condition object in the DMS query builder. It can be used to add conditions to the query.

    Args:
        parent (ET.Element): The parent element to which the condition object will be added.
    """

    def __init__(self, parent: ET.Element) -> None:
        self._parent = parent

    def field_condition(
        self,
        internal_name: str | SystemFields,
        value: DmsQueryFieldValueT,
        operator: DmsQueryConditionFieldOperatorsT = "=",
        bool_op: DmsQueryConditionGroupOperatorsT = "AND",
        dbname: str | None = None,
        name: str | None = None,
    ):
        """Adds a field condition to the current object type or condition object.

        Args:
            internal_name (str | SystemFields): The internal name of the field to condition on.
            value (DmsQueryFieldValueT): The value to compare the field against.
            operator (DmsQueryConditionFieldOperatorsT): The comparison operator. Defaults to ``"="``.
            bool_op (DmsQueryConditionGroupOperatorsT): Logical operator linking this condition to the
                previous one (``"AND"`` or ``"OR"``). XSD default is ``"AND"``.
            dbname (str | None): Database column name — alternative identifier to ``internal_name``.
                Prefer ``internal_name`` whenever possible. Defaults to ``None``.
            name (str | None): Display name — alternative identifier to ``internal_name``.
                Prefer ``internal_name`` whenever possible, as display names may be ambiguous.
                Defaults to ``None``.
        """
        system = "0"

        if isinstance(internal_name, SystemFields):
            internal_name = internal_name.name

        if internal_name in SystemFields.__members__:
            system = "1"

        attribs = {"internal_name": internal_name, "operator": operator, "system": system}
        if bool_op != "AND":
            attribs["bool_op"] = bool_op
        if dbname is not None:
            attribs["dbname"] = dbname
        if name is not None:
            attribs["name"] = name

        field_condition_node = ET.SubElement(self._parent, "FieldCondition", attribs)
        write_field_value(field_condition_node, value)

    def table_condition(self, internal_name: str, row: int = 0):
        """Returns a ``DmsQueryTableCondition`` context manager for filtering on a table field.

        Args:
            internal_name (str): The internal name of the table dialog field.
            row (int): Row index to restrict the condition to. 0 means no row restriction.
                Defaults to 0.

        Returns:
            DmsQueryTableCondition: A table condition builder for this field.
        """
        return DmsQueryTableCondition(self._parent, internal_name, row)

    def creator(self, *values: DmsQueryUserValueT):
        """
        Adds a Creator condition to filter by the user who created the object. The XSD allows a maximum of 2 values.

        Args:
            *values (str | DmsQuerySpecialValue): Up to 2 user name values. Use DmsQuerySpecialValue.BENUTZER for the
                current user.
        """
        creator_node = ET.SubElement(self._parent, "Creator")
        for value in values[:2]:
            _write_user_value(creator_node, value)

    def modifier(self, *values: DmsQueryUserValueT):
        """
        Adds a Modifier condition to filter by the user who last modified the object.

        Args:
            *values (str | DmsQuerySpecialValue): One or more user name values.
        """
        modifier_node = ET.SubElement(self._parent, "Modifier")
        for value in values:
            _write_user_value(modifier_node, value)

    def owner(self, *values: DmsQueryUserValueT, operator: DmsQueryConditionFieldOperatorsT | None = None):
        """Adds an Owner condition to filter by the object owner.

        The boolean operator between multiple values is fixed to ``OR`` by the XSD schema.

        Args:
            *values (str | DmsQuerySpecialValue): One or more owner user name values.
            operator (DmsQueryConditionFieldOperatorsT | None): Optional comparison operator applied to
                all values (e.g. ``"="`` or ``"<>"``). If ``None``, no operator attribute is written.
        """
        owner_node = ET.SubElement(self._parent, "Owner")
        if operator is not None:
            owner_node.attrib["operator"] = operator
        for value in values:
            _write_user_value(owner_node, value)

    def created(self, from_date: date | datetime | str, to_date: date | datetime | str):
        """
        Adds a Created date range condition to filter by the creation date.

        Args:
            from_date (date | datetime | str): Start of the creation date range.
                ``datetime`` → ``DD.MM.YYYY HH:MM:SS``, ``date`` → ``DD.MM.YYYY``, ``str`` unchanged.
            to_date (date | datetime | str): End of the creation date range.
        """
        created_node = ET.SubElement(self._parent, "Created")
        ET.SubElement(created_node, "From").text = _date_to_str(from_date)
        ET.SubElement(created_node, "To").text = _date_to_str(to_date)

    def modified(self, from_date: date | datetime | str, to_date: date | datetime | str):
        """
        Adds a Modified date range condition to filter by the last modification date.

        Args:
            from_date (date | datetime | str): Start of the modification date range.
                ``datetime`` → ``DD.MM.YYYY HH:MM:SS``, ``date`` → ``DD.MM.YYYY``, ``str`` unchanged.
            to_date (date | datetime | str): End of the modification date range.
        """
        modified_node = ET.SubElement(self._parent, "Modified")
        ET.SubElement(modified_node, "From").text = _date_to_str(from_date)
        ET.SubElement(modified_node, "To").text = _date_to_str(to_date)
