"""Ross1000 plain XML serializer.

Generates UTF-8 XML documents from Ross1000MovementDayPayload objects.

Expected output shape per movement day:

    <?xml version="1.0" encoding="UTF-8"?>
    <movimenti struttura="058091-CAV-00001" dal="20260501" al="20260531">
      <movimento data="20260507">
        <struttura>
          <codice>058091-CAV-00001</codice>
          <apertura>1</apertura>
          <camereoccupate>1</camereoccupate>
          <cameredisponibili>6</cameredisponibili>
          <lettidisponibili>20</lettidisponibili>
        </struttura>
        <arrivi>
          <cliente>
            <idswh>c3c4a9a2-12ab</idswh>
            <cognome>ROSSI</cognome>
            <nome>MARIO</nome>
            <sesso>M</sesso>
            <datanascita>19830209</datanascita>
            <cittadinanza>100000100</cittadinanza>
            <statoresidenza>100000100</statoresidenza>
            <statonascita>100000100</statonascita>
          </cliente>
        </arrivi>
        <partenze/>
      </movimento>
    </movimenti>

Node ordering inside <cliente> follows the Liguria Ross1000 specification:
  idswh → cognome → nome → sesso → datanascita →
  cittadinanza → statoresidenza → statonascita
  [→ comune_residenza → provincia_residenza → comune_nascita  (optional)]
"""

from __future__ import annotations

from datetime import date
from io import BytesIO
from xml.etree import ElementTree

from istat.ross1000.models.movement_payload import (
    Ross1000GuestPayload,
    Ross1000MovementDayPayload,
    Ross1000StrutturaPayload,
)


def _sub(parent: ElementTree.Element, tag: str, text: str) -> ElementTree.Element:
    """Append a child element with text content."""
    el = ElementTree.SubElement(parent, tag)
    el.text = text
    return el


def _sub_optional(
    parent: ElementTree.Element,
    tag: str,
    text: str | None,
) -> None:
    """Append a child element only when text is non-empty."""
    if text:
        _sub(parent, tag, text)


def _serialize_struttura(
    parent: ElementTree.Element,
    struttura: Ross1000StrutturaPayload,
) -> None:
    """Append <struttura> block to parent."""
    s = ElementTree.SubElement(parent, "struttura")
    _sub(s, "codice", struttura.codice)
    _sub(s, "apertura", str(struttura.apertura))
    _sub(s, "camereoccupate", str(struttura.camere_occupate))
    _sub(s, "cameredisponibili", str(struttura.camere_disponibili))
    _sub(s, "lettidisponibili", str(struttura.letti_disponibili))


def _serialize_guest(
    parent: ElementTree.Element,
    guest: Ross1000GuestPayload,
) -> None:
    """Append <cliente> block to parent.

    Node order follows the Liguria Ross1000 specification exactly:
      idswh, cognome, nome, sesso, datanascita,
      cittadinanza, statoresidenza, statonascita,
      [comune_residenza, provincia_residenza, comune_nascita]
    """
    c = ElementTree.SubElement(parent, "cliente")

    # Mandatory fields — always emitted
    _sub(c, "idswh", guest.idswh)
    _sub(c, "cognome", guest.cognome)
    _sub(c, "nome", guest.nome)
    _sub(c, "sesso", guest.sesso)
    _sub(c, "datanascita", guest.datanascita)
    _sub(c, "cittadinanza", guest.cittadinanza)
    _sub(c, "statoresidenza", guest.statoresidenza)
    _sub(c, "statonascita", guest.statonascita)

    # Optional fields — emitted only when present
    _sub_optional(c, "comuneresidenza", guest.comune_residenza)
    _sub_optional(c, "provinciaresidenza", guest.provincia_residenza)
    _sub_optional(c, "comunenascita", guest.comune_nascita)


def _serialize_movement_day(
    parent: ElementTree.Element,
    day: Ross1000MovementDayPayload,
) -> None:
    """Append a <movimento> block for a single day."""
    movimento = ElementTree.SubElement(parent, "movimento")
    movimento.set("data", day.date.strftime("%Y%m%d"))

    _serialize_struttura(movimento, day.struttura)

    arrivi_el = ElementTree.SubElement(movimento, "arrivi")
    for guest in day.arrivi:
        _serialize_guest(arrivi_el, guest)

    partenze_el = ElementTree.SubElement(movimento, "partenze")
    for guest in day.partenze:
        _serialize_guest(partenze_el, guest)


def serialize_movement_days_xml(
    days: list[Ross1000MovementDayPayload],
    *,
    structure_code: str,
    start_date: date,
    end_date: date,
) -> str:
    """Serialize a list of movement day payloads to a UTF-8 XML string.

    The root element is <movimenti> with metadata attributes.
    Each day produces one <movimento data="AAAAMMGG"> child.

    Args:
        days:           Movement day payloads (one per calendar day)
        structure_code: ISTAT structure code (for root attribute)
        start_date:     First day of the export range
        end_date:       Last day of the export range

    Returns:
        UTF-8 XML string with XML declaration.
    """
    root = ElementTree.Element("movimenti")
    root.set("struttura", structure_code)
    root.set("dal", start_date.strftime("%Y%m%d"))
    root.set("al", end_date.strftime("%Y%m%d"))

    for day in days:
        _serialize_movement_day(root, day)

    return _to_utf8_xml(root)


def _to_utf8_xml(root: ElementTree.Element) -> str:
    """Serialize an ElementTree to a UTF-8 XML string with declaration."""
    tree = ElementTree.ElementTree(root)
    buf = BytesIO()
    tree.write(buf, encoding="UTF-8", xml_declaration=True, short_empty_elements=False)
    return buf.getvalue().decode("UTF-8")
