"""Veröffentlicht Profil, Fahrten, Tankvorgänge und den Live-Fahrzeugstatus
als pyscript-Zustandsentitäten, damit das Frontend sie über das ganz normale
hass.states lesen kann - kein Service-Aufruf mit Rückgabewert nötig, dessen
genaues Verhalten in der Home-Assistant-Weboberfläche nicht durchgängig
dokumentiert ist (siehe Kopfkommentar in frontend_api.py).

Importierbar mit `import frontend_veroeffentlichung`, aufgerufen sowohl
periodisch (frontend_api.py) als auch direkt nach Schreibaktionen aus
fahrterkennung.py, belegverarbeitung.py und reifenzaehler.py, damit sich die
Oberfläche unmittelbar aktualisiert statt bis zum nächsten Intervall zu
warten.

BEKANNTE GRENZE: Zustands-Attribute in Home Assistant sind nicht für beliebig
große Datenmengen gedacht (~16 KB Richtwert für die Aufzeichnung in der
Recorder-Datenbank). Für das Profil und den aktuellen Stand ist das
unkritisch; die Fahrten- und Tankvorgänge-Archive könnten das nach einigen
Jahren überschreiten. Wird das relevant, müssten fahrten_veroeffentlichen()/
tankvorgaenge_veroeffentlichen() auf einen Zeitraum eingeschränkt werden
(z. B. nur das laufende Jahr), statt hier vorschnell zu vereinfachen.
"""

import einstellungen
import entitaeten
import profil


def profil_veroeffentlichen():
    state.set("pyscript.audi_dashboard_profil", "aktuell", new_attributes={"daten": profil.profil_lesen()})


def fahrten_veroeffentlichen():
    state.set("pyscript.audi_dashboard_fahrten", "aktuell", new_attributes={"daten": profil.fahrten_lesen()})


def tankvorgaenge_veroeffentlichen():
    state.set("pyscript.audi_dashboard_tankvorgaenge", "aktuell", new_attributes={"daten": profil.tankvorgaenge_lesen()})


def batterieverlauf_veroeffentlichen():
    state.set("pyscript.audi_dashboard_batterieverlauf", "aktuell", new_attributes={"daten": profil.batterieverlauf_lesen()})


def entitaeten_veroeffentlichen():
    """Katalog + aktuell wirksame Zuordnung fürs Setup-Menü (Einstellungen ->
    Fahrzeug einrichten -> Setup) - siehe entitaeten.py."""
    state.set("pyscript.audi_dashboard_entitaeten", "aktuell", new_attributes={"daten": entitaeten.aktueller_stand()})


def beleg_ergebnis_veroeffentlichen(daten):
    """Ergebnis eines Beleg-Uploads (geparste Werte fürs Ausfüllen eines
    Entwurfs, oder ein Fehler wie 'Falsches Belegdatum') fürs Frontend, nach
    demselben Zustands-statt-Rückgabewert-Muster wie die übrigen
    veroeffentlichen()-Funktionen hier (siehe Kopfkommentar)."""
    state.set("pyscript.audi_dashboard_beleg_ergebnis", "aktuell", new_attributes={"daten": daten})


def zustand_oder_none(entity_id):
    """Sicherer Zustandszugriff: pyscripts state.get() wirft NameError, wenn
    die Entity (noch) nicht existiert - real an einer Testinstanz beobachtet,
    nicht nur vermutet. Genau das ist hier der Normalfall, solange manche der
    in einstellungen.py hinterlegten Entity-IDs noch Platzhalter sind, und
    bleibt auch danach relevant: fällt eine Datenquelle aus, soll die
    Oberfläche das zeigen (§9), nicht an einem Fehler hängen bleiben."""
    if not entity_id:
        return None
    try:
        wert = state.get(entity_id)
    except NameError:
        return None
    if wert in (None, "unknown", "unavailable"):
        return None
    return wert


def _zu_zahl(wert):
    try:
        return float(wert)
    except (TypeError, ValueError):
        return None


def _abs_zahl(wert):
    zahl = _zu_zahl(wert)
    return None if zahl is None else abs(zahl)


def _standort():
    """Live-GPS-Position des Fahrzeugs (Übersicht -> Standort-Kachel) - liest
    latitude/longitude/gps_accuracy von der in STANDORT_TRACKER hinterlegten
    device_tracker-Entity (HA-Konvention). Fehlt die Entity-ID oder ist sie
    (noch) nicht verfügbar, liefert diese Funktion durchgehend None statt
    eines geratenen Werts - genau das zustand_oder_none()-Muster von oben,
    hier als eigene Funktion, weil zusätzlich zum Zustand noch Attribute und
    der Aktualisierungszeitpunkt gebraucht werden: state.get() liefert dafür
    ein StateVal (str-Unterklasse) mit .last_updated als datetime-Attribut -
    kein eigenständiges state.last_updated(), das gibt es in pyscript nicht."""
    entity_id = einstellungen.STANDORT_TRACKER
    leer = {"lat": None, "lon": None, "genauigkeit_m": None, "zeit": None}
    if not entity_id:
        return leer
    try:
        wert = state.get(entity_id)
        attribute = state.getattr(entity_id)
    except NameError:
        return leer
    if not attribute or attribute.get("latitude") is None:
        return leer
    zeit = None
    try:
        zeit = wert.last_updated.isoformat()
    except AttributeError:
        pass
    return {
        "lat": _zu_zahl(attribute.get("latitude")),
        "lon": _zu_zahl(attribute.get("longitude")),
        "genauigkeit_m": _zu_zahl(attribute.get("gps_accuracy")),
        "zeit": zeit,
    }


POSITIONEN = ["vorne links", "vorne rechts", "hinten links", "hinten rechts"]


def _sicherheitscheck():
    """Liste der einzeln geprüften Punkte hinter "Sicher abgestellt" (§9),
    fürs Frontend (Klick auf den Status öffnet diese Liste mit einem grünen/
    roten/grauen Punkt je Zeile). "ok" ist None, wenn der Sensor fehlt oder
    nicht verfügbar ist - genau daraus leitet sich auch die zusammengefasste
    gesichert-Kennzahl unten ab, damit beide nie auseinanderlaufen können."""
    eintraege = []
    for pos, sensor in zip(POSITIONEN, einstellungen.TUER_SENSOREN):
        w = zustand_oder_none(sensor)
        eintraege.append({"label": f"Tür {pos}", "ok": None if w is None else w == "off"})
    for pos, sensor in zip(POSITIONEN, einstellungen.FENSTER_SENSOREN):
        w = zustand_oder_none(sensor)
        eintraege.append({"label": f"Fenster {pos}", "ok": None if w is None else w == "off"})
    for pos, sensor in zip(POSITIONEN, einstellungen.TUERSCHLOSS_SENSOREN):
        w = zustand_oder_none(sensor)
        eintraege.append({"label": f"Türschloss {pos}", "ok": None if w is None else w == "off"})
    for label, sensor in [
        ("Heckklappe", einstellungen.HECKKLAPPE_SENSOR),
        ("Heckklappenschloss", einstellungen.HECKKLAPPENSCHLOSS_SENSOR),
        ("Motorhaube", einstellungen.HAUBE_SENSOR),
        ("Haubenschloss", einstellungen.HAUBENSCHLOSS_SENSOR),
    ]:
        w = zustand_oder_none(sensor)
        eintraege.append({"label": label, "ok": None if w is None else w == "off"})
    return eintraege


def fahrzeugstatus_veroeffentlichen():
    """Bündelt die live aus Home Assistant gelesenen Fahrzeugwerte
    Frontend-freundlich, damit die Oberfläche keine Entity-IDs kennen muss.
    """
    km = zustand_oder_none(einstellungen.KM_SENSOR)
    tankprozent = zustand_oder_none(einstellungen.TANK_SENSOR)
    reichweite = zustand_oder_none(einstellungen.RANGE_SENSOR)
    batteriespannung = zustand_oder_none(einstellungen.BATTERIE_SENSOR)

    # "Sicher abgestellt" (§9) - erst wenn WIRKLICH jeder einzeln geprüfte
    # Punkt zu/verriegelt ist, gilt das Fahrzeug als gesichert; fehlt auch
    # nur eine Meldung, ist der Status unbekannt statt geraten.
    sicherheitscheck = _sicherheitscheck()
    if any([e["ok"] is None for e in sicherheitscheck]):
        gesichert = None
    else:
        gesichert = all([e["ok"] for e in sicherheitscheck])

    standort = _standort()

    daten = {
        "km": _zu_zahl(km),
        "tankprozent": _zu_zahl(tankprozent),
        "reichweite_km": _zu_zahl(reichweite),
        "batteriespannung": _zu_zahl(batteriespannung),
        "gesichert": gesichert,
        "sicherheitscheck": sicherheitscheck,
        "standort_lat": standort["lat"],
        "standort_lon": standort["lon"],
        "standort_genauigkeit_m": standort["genauigkeit_m"],
        "standort_zeit": standort["zeit"],
        # Vom Fahrzeug selbst gemeldete Service-Fälligkeit (ergänzt die
        # App-eigene Servicebuch-Berechnung, siehe fahrzeugMeldung() im
        # Frontend) - die Streckensensoren liefern negative Restkilometer-
        # Werte, hier deshalb der Betrag.
        "oelwechsel_faellig_ts": zustand_oder_none(einstellungen.NAECHSTER_OELWECHSEL_SENSOR),
        "oelwechsel_faellig_km": _abs_zahl(zustand_oder_none(einstellungen.OELWECHSEL_STRECKE_SENSOR)),
        "inspektion_faellig_ts": zustand_oder_none(einstellungen.NAECHSTE_INSPEKTION_SENSOR),
        "inspektion_faellig_km": _abs_zahl(zustand_oder_none(einstellungen.INSPEKTION_STRECKE_SENSOR)),
    }
    state.set("pyscript.audi_dashboard_fahrzeugstatus", "aktuell", new_attributes={"daten": daten})


def alles_veroeffentlichen():
    profil_veroeffentlichen()
    fahrten_veroeffentlichen()
    tankvorgaenge_veroeffentlichen()
    fahrzeugstatus_veroeffentlichen()
    batterieverlauf_veroeffentlichen()
    entitaeten_veroeffentlichen()
