import xml.dom.minidom as md
from .get import getElement


def _get_or_create_text_child(
    element: md.Element,
    value: str | int | float,
) -> md.Text:
    if element.ownerDocument is None:
        raise ValueError("Element cannot be None")
    child = element.firstChild
    if child is None:
        new_text = element.ownerDocument.createTextNode(str(value))
        element.appendChild(new_text)
        return new_text
    if not isinstance(child, md.Text):
        raise TypeError(f"Tag '{element.tagName}' first child is not a text node")
    return child


def updateXML(
    xml: md.Document | md.Element,
    label: str,
    value: str | int | float,
    idx: int = 0,
) -> None:
    element = getElement(xml, label, el=idx)
    child = _get_or_create_text_child(element, value)
    child.data = str(value)


def updateOrCreateXML(
    xml: md.Document | md.Element,
    label: str,
    value: str | int | float,
    idx: int = 0,
) -> None:
    """Update an existing tag by index, or append it if missing at the tail."""
    if idx < 0:
        raise IndexError("Element index cannot be negative")

    matches = xml.getElementsByTagName(label)
    if idx < len(matches):
        element = matches[idx]
    elif idx == len(matches):
        if isinstance(xml, md.Document):
            parent = xml.documentElement
            owner = xml
        else:
            parent = xml
            owner = xml.ownerDocument

        element = owner.createElement(label)
        parent.appendChild(element)
    else:
        raise IndexError(
            f"Cannot create index {idx} for tag '{label}'. Next valid index is {len(matches)}."
        )

    child = _get_or_create_text_child(element, value)
    child.data = str(value)


def setAttribute(
    xml: md.Document | md.Element,
    label: str,
    attribute: str,
    value: str | int | float,
    idx: int = 0,
) -> None:
    if not isinstance(attribute, str):
        raise TypeError("Attribute name must be a string")
    if not attribute.strip():
        raise ValueError("Attribute name cannot be empty")

    element = getElement(xml, label, el=idx)
    element.setAttribute(attribute, str(value))


def removeTag(
    xml: md.Document | md.Element,
    label: str,
    idx: int = 0,
) -> None:
    element = getElement(xml, label, el=idx)
    parent = element.parentNode
    if parent is None:
        raise AttributeError(f"Tag '{label}' has no parent node")
    parent.removeChild(element)
