"""Abstract base class for typed ``<Field>`` value parsers in DMSContent responses."""
import xml.etree.ElementTree as ET
from abc import ABC, abstractmethod
from datetime import datetime, time

from ecmind_blue_client.ecm import DmsObjectFieldValueTypes
from ecmind_blue_client.ecm.parser._dms_content_object_field_data_types import (
    DmsContentObjectFieldDataTypes,
)


class DmsContentObjectFieldBase(ABC):
    """Abstract base class for all parsed ECM field types.

    Subclasses represent ``<Field>`` (HOL), ``<Column>`` (table dialog), or positional
    row values (LOL), and provide typed access to the underlying XML attribute/text data.
    """

    def __init__(self, node: ET.Element):
        self._node = node

    @abstractmethod
    def _getRawValue(self) -> str | None:
        """Returns the raw string value of this field, or ``None`` if the field is empty."""

    @property
    @abstractmethod
    def display_value(self) -> str:
        """Human-readable display value as returned by the server."""

    def typed_value(self, value_type: type[DmsObjectFieldValueTypes]) -> DmsObjectFieldValueTypes | None:
        """Returns the field value cast to *value_type*, or ``None`` if the field is empty.

        Args:
            value_type: The expected Python type (e.g. ``int``, ``str``, ``datetime``).

        Returns:
            The typed value, or ``None`` if the raw value is absent.

        Raises:
            ValueError: If the parsed value does not match *value_type*.
        """
        value = self.value()

        if value is None:
            return None

        if not isinstance(value, value_type):
            raise ValueError(f"Value {value} is not of type {value_type}")
        return value

    def value(self) -> DmsObjectFieldValueTypes:
        """Returns the field value converted to the appropriate Python type.

        Conversion is based on the ``datatype`` attribute (see ``DmsContentObjectFieldDataTypes``).

        Returns:
            The converted value, or ``None`` if the raw value is absent.

        Raises:
            ValueError: If the datatype is not supported.
        """
        value = self._getRawValue()

        if value is None:
            return None

        if self.datatype == DmsContentObjectFieldDataTypes.TEXT:
            return value

        if self.datatype == DmsContentObjectFieldDataTypes.INTEGER:
            return int(value)

        if self.datatype == DmsContentObjectFieldDataTypes.SMALLINT:
            return int(value)

        if self.datatype == DmsContentObjectFieldDataTypes.DECIMAL:
            return float(value)

        if self.datatype == DmsContentObjectFieldDataTypes.DATETIME:
            value = int(value)
            return datetime.fromtimestamp(value)

        if self.datatype == DmsContentObjectFieldDataTypes.DATE:
            return datetime.strptime(value, "%d.%m.%Y")

        if self.datatype == DmsContentObjectFieldDataTypes.TIME:
            value = int(value)
            hour = int(value / 3600)
            minute = int((value - (hour * 3600)) / 60)
            second = int(value - (hour * 3600) - (minute * 60))

            return time(hour=hour, minute=minute, second=second)

        raise ValueError(f"Unsupported data type {self.datatype}")

    @property
    def internal_name(self) -> str:
        """Internal (programmatic) name of this field."""
        return self._node.get("internal_name", "")

    @property
    def name(self) -> str:
        """Display name of this field as configured in the server."""
        return self._node.get("name", "")

    @property
    def ostype(self) -> str:
        """Object storage type string of this field."""
        return self._node.get("ostype", "")

    @property
    def osguid(self) -> str:
        """Object storage GUID of this field."""
        return self._node.get("osguid", "")

    @property
    def dbname(self) -> str:
        """Database column name backing this field."""
        return self._node.get("dbname", "")

    @property
    def datatype(self) -> DmsContentObjectFieldDataTypes:
        """Data type of this field (e.g. ``TEXT``, ``INTEGER``, ``DATETIME``)."""
        return DmsContentObjectFieldDataTypes(self._node.get("datatype", ""))

    @property
    def size(self) -> int:
        """Maximum character length of this field (``0`` if not applicable)."""
        return int(self._node.get("size", 0))

    @property
    def type(self) -> str:
        """Field type string as returned by the server (e.g. ``COMBO``, ``TEXT``)."""
        return self._node.get("type", "")

    @property
    def visible(self) -> int:
        """Visibility flag: ``1`` if the field is visible in the UI, ``0`` otherwise."""
        return int(self._node.get("visible", "1"))

    @property
    def sortpos(self) -> int:
        """Sort position of the field within its parent object type."""
        return int(self._node.get("sortpos", "0"))

    @property
    def object(self) -> str:
        """Reference to a linked object (e.g. a lookup table) associated with this field."""
        return self._node.get("object", "")
