"""Builder for ``<TableField>`` elements within a DMSData ``<Object>``."""
import xml.etree.ElementTree as ET
from typing import Optional

from ecmind_blue_client.ecm import DmsObjectFieldValueTypes
from ecmind_blue_client.ecm._common import dms_object_field_value_to_str
from ecmind_blue_client.ecm.builder._common import DmsDataFieldFunction


class DmsDataTableField:
    """Represents a ``<TableField>`` element in the DMSData XML for table dialog fields.

    The XSD supports two mutually exclusive row formats — choose one per instance:

    **Named format** (``row()``):
    Each row contains ``<Field internal_name="…">`` elements. Use when field names are
    known at build time.

    .. code-block:: python

        with obj.table_field("my_table") as tf:
            tf.row({"col_a": "value1", "col_b": 42})

    **Positional format** (``add_column()`` + ``row_values()``):
    Column definitions are declared once via ``<Columns>/<Column>``; rows are added as
    positional ``<Rows>/<Row>/<Value>`` elements. Use for bulk imports or when column
    order is fixed.

    .. code-block:: python

        with obj.table_field("my_table") as tf:
            tf.add_column(internal_name="col_a")
            tf.add_column(internal_name="col_b", name="Column B")
            tf.row_values(["value1", 42])
            tf.row_values(["value3", 99], line=2)
    """

    def __init__(self, parent: ET.Element, internal_name: str) -> None:
        """
        Args:
            parent (ET.Element): Parent XML element (``<TableFields>``).
            internal_name (str): Internal name of the table dialog field.
        """
        self._parent_node = parent
        self._internal_name = internal_name

        self._node = ET.SubElement(self._parent_node, "TableField", {"internal_name": internal_name})

    def row(self, columns: dict[str, DmsObjectFieldValueTypes | DmsDataFieldFunction], line: Optional[int] = None):
        """Adds a ``<Row>`` element with one named ``<Field>`` per column (named format).

        Args:
            columns (dict[str, DmsObjectFieldValueTypes | DmsDataFieldFunction]): Mapping of
                column internal names to their values. Use ``DmsDataFieldFunction`` members for
                server-side function placeholders (e.g. ``DmsDataFieldFunction.NULL``).
            line (Optional[int]): 1-based row line number for update/delete operations on existing
                rows. ``None`` appends a new row. Defaults to ``None``.
        """
        row_node = ET.SubElement(self._node, "Row")
        if line is not None:
            row_node.attrib["line"] = str(line)

        for internal_name, column in columns.items():
            field_node = ET.SubElement(row_node, "Field", {"internal_name": internal_name})
            if isinstance(column, DmsDataFieldFunction):
                field_node.attrib["field_function"] = column.value
            else:
                field_node.text = dms_object_field_value_to_str(column)

    def add_column(
        self,
        internal_name: Optional[str] = None,
        name: Optional[str] = None,
        dbname: Optional[str] = None,
    ):
        """Adds a ``<Column>`` definition to the ``<Columns>`` section (positional format).

        Must be called before ``row_values()``. Cannot be mixed with ``row()``.
        Columns are declared in the order they will appear in each ``<Row>``.

        Args:
            internal_name (Optional[str]): Internal name of the column field.
            name (Optional[str]): Display name of the column field.
            dbname (Optional[str]): Database column name of the field.
        """
        columns_node = self._node.find("Columns")
        if columns_node is None:
            columns_node = ET.SubElement(self._node, "Columns")

        attribs: dict[str, str] = {}
        if name is not None:
            attribs["name"] = name
        if internal_name is not None:
            attribs["internal_name"] = internal_name
        if dbname is not None:
            attribs["dbname"] = dbname

        ET.SubElement(columns_node, "Column", attribs)

    def row_values(
        self,
        values: list[DmsObjectFieldValueTypes | DmsDataFieldFunction],
        line: Optional[int] = None,
    ):
        """Adds a ``<Row>`` with positional ``<Value>`` elements to the ``<Rows>`` section (positional format).

        ``add_column()`` must be called first to define the column order.
        The number of values should match the number of columns declared via ``add_column()``.

        Args:
            values (list[DmsObjectFieldValueTypes | DmsDataFieldFunction]): Positional values
                in column declaration order. Use ``DmsDataFieldFunction`` members for server-side
                function placeholders (e.g. ``DmsDataFieldFunction.NULL``).
            line (Optional[int]): 1-based row line number for update/delete operations on existing
                rows. ``None`` appends a new row. Defaults to ``None``.
        """
        rows_node = self._node.find("Rows")
        if rows_node is None:
            rows_node = ET.SubElement(self._node, "Rows")

        row_node = ET.SubElement(rows_node, "Row")
        if line is not None:
            row_node.attrib["line"] = str(line)

        for value in values:
            value_node = ET.SubElement(row_node, "Value")
            if isinstance(value, DmsDataFieldFunction):
                value_node.attrib["field_function"] = value.value
            else:
                value_node.text = dms_object_field_value_to_str(value)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass
