"""Die Entitäten, über die beide Oberflächen die Daten der App lesen.

Das Frontend liest hass.states["sensor.audi_dashboard_profil"] usw. direkt und
holt sich die Nutzlast aus dem Attribut `daten` - kein Dienstaufruf mit
Rückgabewert, dessen Verhalten aus Sicht des Browsers nicht durchgängig
dokumentiert ist.

WARUM ECHTE ENTITÄTEN UND NICHT NUR EINTRÄGE IN DER ZUSTANDSMASCHINE
--------------------------------------------------------------------
Wegen einer einzigen Zeile: `_unrecorded_attributes`. Die Nutzlast dieser
Entitäten ist groß - das Fahrtenarchiv wächst über die Jahre auf Hunderte
Kilobyte. Home Assistant zeichnet Zustands-Attribute standardmäßig bei jeder
Änderung in der Recorder-Datenbank auf und warnt ab ~16 KB, dass genau das
Datenbankprobleme macht. In der pyscript-Fassung war das eine offene Flanke:
jede Veröffentlichung schrieb den kompletten Bestand erneut in die Datenbank,
alle 60 Sekunden.

Nur eine Entität, die zu einer Integration gehört, kann Attribute von der
Aufzeichnung ausnehmen. Ein roher Eintrag in der Zustandsmaschine (was
pyscripts state.set() macht) kann das nicht. Die Nutzlast geht damit
weiterhin an jede Oberfläche, landet aber nie in der Datenbank.

Die Zustände selbst sind bewusst schlicht ("aktuell"): der Inhalt steckt im
Attribut, der Zustand sagt nur, dass etwas da ist. Bei den drei
Reifen-Entitäten und der App-Version ist der Zustand dagegen der eigentliche
Wert - die sind zur Kontrolle in Entwicklerwerkzeuge -> Zustände und für
eigene Automationen des Nutzers gedacht.
"""

from __future__ import annotations

from dataclasses import dataclass

from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfLength
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import (
    DOMAIN,
    E_APP_VERSION,
    E_BATTERIEVERLAUF,
    E_BELEG_ERGEBNIS,
    E_FAHRTEN,
    E_FAHRZEUGSTATUS,
    E_IMPORT_STATUS,
    E_PROFIL,
    E_REIFEN_AKTIV,
    E_REIFEN_SOMMER,
    E_REIFEN_WINTER,
    E_TANKVORGAENGE,
    E_ZUORDNUNG,
    SIGNAL_AKTUALISIERT,
)
from .koordinator import Koordinator


@dataclass(frozen=True)
class Beschreibung:
    schluessel: str
    name: str
    icon: str
    einheit: str | None = None
    kategorie: EntityCategory | None = None


BESCHREIBUNGEN: tuple[Beschreibung, ...] = (
    Beschreibung(E_PROFIL, "Audi Dashboard Profil", "mdi:car-info"),
    Beschreibung(E_FAHRTEN, "Audi Dashboard Fahrten", "mdi:road-variant"),
    Beschreibung(E_TANKVORGAENGE, "Audi Dashboard Tankvorgänge", "mdi:gas-station"),
    Beschreibung(E_FAHRZEUGSTATUS, "Audi Dashboard Fahrzeugstatus", "mdi:car-connected"),
    Beschreibung(E_BATTERIEVERLAUF, "Audi Dashboard Batterieverlauf", "mdi:car-battery"),
    Beschreibung(E_REIFEN_SOMMER, "Audi Dashboard Reifen Sommer km", "mdi:tire", UnitOfLength.KILOMETERS),
    Beschreibung(E_REIFEN_WINTER, "Audi Dashboard Reifen Winter km", "mdi:snowflake", UnitOfLength.KILOMETERS),
    Beschreibung(E_REIFEN_AKTIV, "Audi Dashboard Reifen aktiver Satz", "mdi:tire"),
    Beschreibung(E_ZUORDNUNG, "Audi Dashboard Entitäten", "mdi:link-variant", None, EntityCategory.DIAGNOSTIC),
    Beschreibung(E_BELEG_ERGEBNIS, "Audi Dashboard Beleg Ergebnis", "mdi:receipt-text", None, EntityCategory.DIAGNOSTIC),
    Beschreibung(E_IMPORT_STATUS, "Audi Dashboard Import Status", "mdi:database-import", None, EntityCategory.DIAGNOSTIC),
    Beschreibung(E_APP_VERSION, "Audi Dashboard App Version", "mdi:tag-outline", None, EntityCategory.DIAGNOSTIC),
)


async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    koordinator: Koordinator = entry.runtime_data
    async_add_entities(
        AudiEntitaet(koordinator, beschreibung) for beschreibung in BESCHREIBUNGEN
    )


class AudiEntitaet(SensorEntity):
    """Eine Entität je Datenbestand. Zustand kurz, Inhalt im Attribut `daten`."""

    # Der eigentliche Grund für echte Entitäten - siehe Kopfkommentar.
    _unrecorded_attributes = frozenset({"daten"})

    # Bewusst KEIN has_entity_name: die Entity-IDs (sensor.audi_dashboard_*)
    # sind ein Vertrag mit beiden Oberflächen (siehe const.py). Mit
    # has_entity_name würde Home Assistant den Gerätenamen davorsetzen und die
    # IDs hingen am - vom Nutzer änderbaren - Gerätenamen.
    _attr_has_entity_name = False
    _attr_should_poll = False

    def __init__(self, koordinator: Koordinator, beschreibung: Beschreibung) -> None:
        self._koordinator = koordinator
        self._beschreibung = beschreibung
        self._attr_name = beschreibung.name
        self._attr_icon = beschreibung.icon
        self._attr_native_unit_of_measurement = beschreibung.einheit
        self._attr_entity_category = beschreibung.kategorie
        self._attr_unique_id = f"{koordinator.entry.entry_id}_{beschreibung.schluessel}"
        # Die gewünschte Objekt-ID ausdrücklich vorgeben, statt sie aus dem
        # Namen ableiten zu lassen: aus dem Namen käme zwar dasselbe heraus,
        # aber nur solange niemand den Namen anfasst.
        self.internal_integration_suggested_object_id = (
            f"audi_dashboard_{beschreibung.schluessel}"
        )
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, koordinator.entry.entry_id)},
            name="Audi Dashboard",
            manufacturer="Audi Dashboard",
            sw_version=koordinator.version,
            entry_type=None,
        )

    async def async_added_to_hass(self) -> None:
        self.async_on_remove(
            async_dispatcher_connect(
                self.hass,
                f"{SIGNAL_AKTUALISIERT}_{self._beschreibung.schluessel}",
                self._neu_zeichnen,
            )
        )

    @callback
    def _neu_zeichnen(self) -> None:
        self.async_write_ha_state()

    def _wert(self) -> tuple[str, object]:
        return self._koordinator.werte.get(self._beschreibung.schluessel, ("unbekannt", None))

    @property
    def available(self) -> bool:
        """Bis zur ersten Veröffentlichung gibt es nichts zu zeigen.

        "unavailable" ist hier ehrlicher als ein Platzhalterwert: die
        Oberfläche unterscheidet ausdrücklich zwischen "kein Wert" und "Wert
        unbekannt" und darf sich darauf verlassen."""
        return self._beschreibung.schluessel in self._koordinator.werte

    @property
    def native_value(self) -> str:
        return self._wert()[0]

    @property
    def extra_state_attributes(self) -> dict[str, object]:
        return {"daten": self._wert()[1]}
