"""Reifenzähler: jeder gefahrene Kilometer wird dem Satz zugerechnet, der zum
Zeitpunkt der Fahrt aktiv war.

Früher war der Zähler = aktueller Kilometerstand minus fester Startwert je
Satz. Das war falsch, sobald einmal zwischen Sommer und Winter umgeschaltet
wurde: die Differenz zum Startwert zählt dann alle seither gefahrenen
Kilometer mit, auch die auf dem jeweils anderen Satz. Jetzt führt
reifen.saetze.<satz>.km selbst den fortlaufenden Gesamtstand, und
reifen.referenz_odo_km hält den zuletzt verarbeiteten Kilometerstand fest,
gegen den die nächste Änderung als Delta gemessen wird.

Keine Abhängigkeit von total_increasing oder utility_meter: die Fortschreibung
braucht nur den zuletzt bekannten Absolutwert des Kilometerstand-Sensors. Ein
zeitweise "unavailable" Quellsensor verliert dadurch nichts - referenz_odo_km
bleibt einfach stehen, bis wieder ein gültiger Wert kommt, und das nächste
Delta wird dann gegen diesen alten Stand berechnet. Auch Kilometer, die
während eines HA-Ausfalls gefahren wurden, werden so noch dem damals aktiven
Satz zugerechnet.
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from .veroeffentlichung import zustand_oder_none

if TYPE_CHECKING:
    from .koordinator import Koordinator

_LOGGER = logging.getLogger(__name__)

SAETZE = ("sommer", "winter")


def aktueller_km(k: Koordinator) -> float | None:
    try:
        return float(zustand_oder_none(k.hass, k.zuordnung.werte.KM_SENSOR))  # type: ignore[arg-type]
    except (TypeError, ValueError):
        return None


async def fortschreiben(k: Koordinator, neuer_km: float | None, neuer_aktiv: str | None = None) -> None:
    """Rechnet zuerst mit dem bisher aktiven Satz ab (Delta seit
    referenz_odo_km), trägt danach optional einen Satzwechsel ein - in dieser
    Reihenfolge, damit beim Wechseln selbst keine Kilometer dem neuen Satz
    zugerechnet werden, die eigentlich noch auf dem alten gefahren wurden."""
    profil = await k.ablage.profil_lesen()
    if profil is None:
        return

    reifen = profil.setdefault("reifen", {})
    saetze = reifen.setdefault("saetze", {})
    aktiv = reifen.get("aktiv", "sommer")
    referenz = reifen.get("referenz_odo_km")

    if referenz is not None and neuer_km is not None:
        delta = round(neuer_km - referenz, 1)
        if delta > 0:
            satz = saetze.setdefault(aktiv, {})
            satz["km"] = round((satz.get("km") or 0) + delta, 1)
    if neuer_km is not None:
        reifen["referenz_odo_km"] = neuer_km
    if neuer_aktiv is not None:
        reifen["aktiv"] = neuer_aktiv

    await k.ablage.profil_schreiben(profil)
    await k.profil_veroeffentlichen(profil)


async def wechseln(k: Koordinator, satz: str | None) -> None:
    if satz not in SAETZE:
        _LOGGER.warning("Ungültiger Reifensatz '%s'", satz)
        return
    await fortschreiben(k, aktueller_km(k), neuer_aktiv=satz)
    _LOGGER.info("Aktiver Reifensatz auf '%s' umgeschaltet", satz)


async def km_setzen(k: Koordinator, satz: str | None, km: object) -> None:
    """Manuelle Korrektur des gefahrenen Kilometerstands eines Reifensatzes.

    Überschreibt nur den gespeicherten Zähler selbst - referenz_odo_km (der
    zuletzt verarbeitete Fahrzeug-Kilometerstand) bleibt unverändert, damit
    die nächste Fortschreibung weiterhin korrekt von hier aus weiterzählt,
    statt auf den neuen Wert erneut ein Delta seit dem alten Stand
    aufzuschlagen."""
    if satz not in SAETZE:
        _LOGGER.warning("Ungültiger Reifensatz '%s'", satz)
        return
    try:
        wert = round(float(km), 1)  # type: ignore[arg-type]
    except (TypeError, ValueError):
        _LOGGER.warning("Ungültiger km-Wert '%s'", km)
        return
    if wert < 0:
        _LOGGER.warning("Negativer km-Wert '%s' abgelehnt", wert)
        return

    profil = await k.ablage.profil_lesen()
    if profil is None:
        return
    reifen = profil.setdefault("reifen", {})
    reifen.setdefault("saetze", {}).setdefault(satz, {})["km"] = wert
    await k.ablage.profil_schreiben(profil)
    await k.profil_veroeffentlichen(profil)
    _LOGGER.info("km-Stand '%s' manuell auf %s gesetzt", satz, wert)


def zustaende(profil: dict | None) -> dict[str, object]:
    """Die drei Kontrollwerte für die eigenen Reifen-Entitäten.

    Das Frontend liest die Zahlen direkt aus dem veröffentlichten Profil -
    diese Entitäten sind nur zur Kontrolle in Entwicklerwerkzeuge -> Zustände
    und für eigene Automationen des Nutzers da."""
    reifen = (profil or {}).get("reifen", {})
    saetze = reifen.get("saetze", {})
    return {
        "sommer": saetze.get("sommer", {}).get("km"),
        "winter": saetze.get("winter", {}).get("km"),
        "aktiv": reifen.get("aktiv") or "unbekannt",
    }
