from datetime import time, timedelta
from pathlib import Path
import xml.etree.ElementTree as ET
from luzapp.models.school import SchoolConfig, Group, Track, ClassRoom, StaffGroup, Person
from luzapp.models.color import NamedColor


def serialize_school(config: SchoolConfig) -> str:
    """Serialize a :class:`~luzapp.models.school.SchoolConfig` to XML.

    The output is compatible with the ``config.school`` file format consumed
    by the LuzApp .NET application.

    Args:
        config: The school configuration to serialize.

    Returns:
        A UTF-8 XML string representing the school configuration.
    """
    root = ET.Element("school", name=config.name)
    root.set("sync-server", config.sync_server)

    elements = ET.SubElement(root, "elements")

    groups_el = ET.SubElement(elements, "groups")
    for g in config.groups:
        ET.SubElement(groups_el, "group", name=g.name, size=str(g.size))

    tracks_el = ET.SubElement(elements, "tracks")
    for t in config.tracks:
        ET.SubElement(tracks_el, "track", name=t.name)

    classes_el = ET.SubElement(elements, "classes")
    for c in config.classes:
        ET.SubElement(
            classes_el,
            "class",
            name=c.name,
            seats=str(c.seats),
            workstations=str(c.workstations),
        )

    ET.SubElement(root, "defaultclass", name=config.default_class)

    segel_el = ET.SubElement(root, "segel")
    for sg in config.staff_groups:
        sg_el = ET.SubElement(segel_el, "group", name=sg.name)
        for p in sg.members:
            ET.SubElement(sg_el, "person", name=p.name, sex=p.sex)

    ET.SubElement(root, "week", days=str(config.week_days))

    total_seconds = int(config.day_length.total_seconds())
    length_hours, remainder = divmod(total_seconds, 3600)
    length_minutes = remainder // 60
    ET.SubElement(
        root,
        "day",
        start=config.day_start.strftime("%H:%M"),
        length=f"{length_hours:02d}:{length_minutes:02d}",
    )

    hours_el = ET.SubElement(root, "hours")
    for t in config.time_grid:
        ET.SubElement(hours_el, "line", time=t.strftime("%H:%M"))

    colors_el = ET.SubElement(root, "colors")
    for c in config.colors:
        ET.SubElement(
            colors_el,
            "color",
            name=c.name,
            R=str(c.r),
            G=str(c.g),
            B=str(c.b),
        )

    ET.indent(root)
    return ET.tostring(root, encoding="unicode", xml_declaration=False)


def parse_school(source: str | Path) -> SchoolConfig:
    """Parse a :class:`~luzapp.models.school.SchoolConfig` from a ``config.school`` file or XML string.

    Args:
        source: Either a :class:`~pathlib.Path` to a ``config.school`` file or
            a raw XML string.
    """
    if isinstance(source, Path):
        root = ET.parse(source).getroot()
    else:
        root = ET.fromstring(source)

    name = root.attrib.get("name", "")
    sync_server = root.attrib.get("sync-server", "")

    elements = root.find("elements")

    if elements is not None:
        groups_el = elements.find("groups")
        groups = tuple(
            Group(name=g.attrib["name"], size=int(g.attrib.get("size", 0)))
            for g in (list(groups_el) if groups_el is not None else [])
            if g.tag == "group"
        )
        tracks_el = elements.find("tracks")
        tracks = tuple(
            Track(name=t.attrib["name"])
            for t in (list(tracks_el) if tracks_el is not None else [])
            if t.tag == "track"
        )
        classes_el = elements.find("classes")
        classes = tuple(
            ClassRoom(
                name=c.attrib["name"],
                seats=int(c.attrib.get("seats", 0)),
                workstations=int(c.attrib.get("workstations", 0)),
            )
            for c in (list(classes_el) if classes_el is not None else [])
            if c.tag == "class"
        )
    else:
        groups: tuple[Group, ...] = ()
        tracks: tuple[Track, ...] = ()
        classes: tuple[ClassRoom, ...] = ()

    default_class_el = root.find("defaultclass")
    default_class = default_class_el.attrib.get("name", "") if default_class_el is not None else ""

    segel_el = root.find("segel")
    staff_groups: tuple[StaffGroup, ...] = ()
    if segel_el is not None:
        staff_groups = tuple(
            StaffGroup(
                name=sg.attrib["name"],
                members=tuple(
                    Person(name=p.attrib["name"], sex=p.attrib.get("sex", ""))
                    for p in sg.findall("person")
                ),
            )
            for sg in segel_el.findall("group")
        )

    week_el = root.find("week")
    week_days = int(week_el.attrib.get("days", 7)) if week_el is not None else 7

    day_el = root.find("day")
    if day_el is not None:
        start_parts = day_el.attrib["start"].split(":")
        day_start = time(int(start_parts[0]), int(start_parts[1]))
        length_parts = day_el.attrib["length"].split(":")
        day_length = timedelta(hours=int(length_parts[0]), minutes=int(length_parts[1]))
    else:
        day_start = time(8, 0)
        day_length = timedelta(hours=12)

    hours_el = root.find("hours")
    time_grid: tuple[time, ...] = ()
    if hours_el is not None:
        time_grid = tuple(
            time(*map(int, line.attrib["time"].split(":")))
            for line in hours_el.findall("line")
        )

    colors_el = root.find("colors")
    colors: tuple[NamedColor, ...] = ()
    if colors_el is not None:
        colors = tuple(
            NamedColor(
                name=c.attrib["name"],
                r=int(c.attrib["R"]),
                g=int(c.attrib["G"]),
                b=int(c.attrib["B"]),
            )
            for c in colors_el.findall("color")
        )

    return SchoolConfig(
        name=name,
        sync_server=sync_server,
        groups=groups,
        tracks=tracks,
        classes=classes,
        staff_groups=staff_groups,
        default_class=default_class,
        week_days=week_days,
        day_start=day_start,
        day_length=day_length,
        time_grid=time_grid,
        colors=colors,
    )
