"""Update-Funktion für die Oberfläche ("Update suchen" in Einstellungen).

Voraussetzung: einstellungen.UPDATE_REPO_URL zeigt auf ein Git-Repository, in
das dieses Projekt (pyscript/ + www/, wie mit update.ps1 auf die eigene
Testinstanz gespielt) gepflegt/gepusht wird - z. B. ein privates GitHub-Repo,
genau wie es für die HACS-Integration bereits genutzt wird. Ohne eingetragene
URL meldet "Update suchen" nur "keine Update-Quelle eingerichtet" (§9-
Fallback), es passiert nichts von selbst.

Kopiert wie update.ps1 bewusst NUR Code:
  - pyscript/ komplett (kein Nutzerdaten-Ordner dort)
  - www/*.js, www/*.css, www/badges/*
Bewusst NICHT angefasst: www/bilder/ (vom Nutzer hochgeladene Fahrzeugfotos,
siehe bilderverwaltung.py) und alles unter audi_dashboard/ (Fahrzeugprofil,
Fahrten- und Tankvorgänge-Archive) - ein Update darf niemals echte
Fahrzeugdaten oder eigene Fotos überschreiben.

Vor jeder Installation wird der bisherige Code-Stand unter
audi_dashboard/code_backups/<Zeitstempel>/ gesichert (gleiches Muster wie
backup.py für die Nutzerdaten) - kein automatisches Rollback in der
Oberfläche, aber ein manueller Weg zurück, falls ein Update Probleme macht.

git ist im offiziellen Home-Assistant-Container vorhanden (an der
Testinstanz geprüft) und subprocess ist über allow_all_imports bereits
freigegeben (siehe configuration_snippet.yaml), deshalb hier statt eines
selbstgebauten ZIP-Downloads einfach ein flacher Git-Checkout in einen
eigenen Zwischenordner, getrennt von /config/audi_dashboard (Nutzerdaten)
und /config/pyscript, /config/www (Live-Code).
"""

import datetime
import io
import json
import os
import shutil
import subprocess

import einstellungen
import profil

QUELLE_ORDNER = "/config/.audi_dashboard_update_quelle"
BACKUP_ORDNER = f"{profil.BASIS}/code_backups"
LIVE_PYSCRIPT = "/config/pyscript"
LIVE_WWW = "/config/www"
CODE_DATEIEN_WWW = ["audi-dashboard-app.js", "audi-dashboard-panel.js", "audi-dashboard.css"]


def _status_veroeffentlichen(daten):
    state.set("pyscript.audi_dashboard_update_status", "aktuell", new_attributes={"daten": daten})


def _git(*args, cwd=None):
    # subprocess.run() direkt, ohne task.executor - genau wie der bestehende
    # PDF-Parser-Aufruf in belegverarbeitung.py (_parser_aufrufen), an der
    # Testinstanz bereits bestätigt funktionsfähig.
    return subprocess.run(
        ["git", *args],
        cwd=cwd, capture_output=True, text=True, timeout=60,
    )


def _lokale_version():
    pfad = f"{LIVE_WWW}/audi-dashboard-version.json"
    if not os.path.exists(pfad):
        return None
    try:
        f = task.executor(io.open, pfad, "r")
        inhalt = f.read()
        f.close()
        return json.loads(inhalt).get("version")
    except (ValueError, OSError):
        return None


def _quelle_aktualisieren():
    """Klont bzw. holt den konfigurierten Branch flach in QUELLE_ORDNER.
    Gibt (erfolgreich, fehlertext) zurück."""
    zweig = einstellungen.UPDATE_BRANCH or "main"
    if not os.path.exists(f"{QUELLE_ORDNER}/.git"):
        os.makedirs(os.path.dirname(QUELLE_ORDNER), exist_ok=True)
        if os.path.exists(QUELLE_ORDNER):
            task.executor(shutil.rmtree, QUELLE_ORDNER)
        r = _git("clone", "--depth", "1", "--branch", zweig, einstellungen.UPDATE_REPO_URL, QUELLE_ORDNER)
        if r.returncode != 0:
            return False, r.stderr.strip() or "git clone fehlgeschlagen"
        return True, None
    r = _git("fetch", "--depth", "1", "origin", zweig, cwd=QUELLE_ORDNER)
    if r.returncode != 0:
        return False, r.stderr.strip() or "git fetch fehlgeschlagen"
    r = _git("reset", "--hard", f"origin/{zweig}", cwd=QUELLE_ORDNER)
    if r.returncode != 0:
        return False, r.stderr.strip() or "git reset fehlgeschlagen"
    return True, None


@service
def audi_dashboard_update_pruefen():
    """Prüft, ob unter einstellungen.UPDATE_REPO_URL eine neuere Version
    vorliegt, ohne etwas zu installieren. Aufruf als
    pyscript.audi_dashboard_update_pruefen."""
    jetzt = datetime.datetime.now().isoformat()
    if not einstellungen.UPDATE_REPO_URL:
        _status_veroeffentlichen({"stand": "keine_quelle", "geprueft_um": jetzt})
        return
    ok, fehler = _quelle_aktualisieren()
    if not ok:
        _status_veroeffentlichen({
            "stand": "fehler", "fehler": fehler, "geprueft_um": jetzt,
            "aktuelle_version": _lokale_version(),
        })
        log.warning(f"audi_dashboard: Update-Prüfung fehlgeschlagen: {fehler}")
        return
    quell_version_pfad = f"{QUELLE_ORDNER}/www/audi-dashboard-version.json"
    neue_version = None
    if os.path.exists(quell_version_pfad):
        f = task.executor(io.open, quell_version_pfad, "r")
        inhalt = f.read()
        f.close()
        try:
            neue_version = json.loads(inhalt).get("version")
        except ValueError:
            neue_version = None
    aktuelle_version = _lokale_version()
    verfuegbar = neue_version is not None and aktuelle_version is not None and neue_version > aktuelle_version
    _status_veroeffentlichen({
        "stand": "geprueft", "verfuegbar": verfuegbar,
        "aktuelle_version": aktuelle_version, "neue_version": neue_version,
        "geprueft_um": jetzt,
    })
    log.info(f"audi_dashboard: Update-Prüfung ok - aktuell={aktuelle_version}, verfügbar={neue_version}")


@service
def audi_dashboard_update_installieren():
    """Installiert den zuletzt per audi_dashboard_update_pruefen geholten
    Stand aus QUELLE_ORDNER - sichert vorher den bisherigen Code. Aufruf als
    pyscript.audi_dashboard_update_installieren."""
    if not os.path.exists(f"{QUELLE_ORDNER}/.git"):
        log.warning("audi_dashboard: Update-Installation ohne vorherige Prüfung abgelehnt")
        return

    zeitstempel = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    ziel_backup = f"{BACKUP_ORDNER}/{zeitstempel}"
    os.makedirs(ziel_backup, exist_ok=True)
    if os.path.exists(LIVE_PYSCRIPT):
        task.executor(shutil.copytree, LIVE_PYSCRIPT, f"{ziel_backup}/pyscript")
    for name in CODE_DATEIEN_WWW:
        quelle = f"{LIVE_WWW}/{name}"
        if os.path.exists(quelle):
            task.executor(shutil.copy, quelle, f"{ziel_backup}/{name}")

    quelle_pyscript = f"{QUELLE_ORDNER}/pyscript"
    if os.path.exists(quelle_pyscript):
        if os.path.exists(LIVE_PYSCRIPT):
            task.executor(shutil.rmtree, LIVE_PYSCRIPT)
        task.executor(shutil.copytree, quelle_pyscript, LIVE_PYSCRIPT)

    for name in CODE_DATEIEN_WWW:
        quelle = f"{QUELLE_ORDNER}/www/{name}"
        if os.path.exists(quelle):
            task.executor(shutil.copy, quelle, f"{LIVE_WWW}/{name}")

    quelle_badges = f"{QUELLE_ORDNER}/www/badges"
    if os.path.exists(quelle_badges):
        ziel_badges = f"{LIVE_WWW}/badges"
        if os.path.exists(ziel_badges):
            task.executor(shutil.rmtree, ziel_badges)
        task.executor(shutil.copytree, quelle_badges, ziel_badges)

    # Version neu schreiben (aktuelle Uhrzeit als Cache-Buster, wie
    # update.ps1) statt den im Quell-Repo eingecheckten Wert blind zu
    # übernehmen - erzwingt bei jedem Browser einen echten Neuabruf von
    # audi-dashboard-app.js/.css (siehe audi-dashboard-panel.js).
    version = int(datetime.datetime.now().timestamp())
    f = task.executor(io.open, f"{LIVE_WWW}/audi-dashboard-version.json", "w")
    f.write(json.dumps({"version": version}))
    f.close()

    _status_veroeffentlichen({
        "stand": "installiert", "verfuegbar": False,
        "aktuelle_version": version, "neue_version": None,
        "geprueft_um": datetime.datetime.now().isoformat(),
    })
    log.info(f"audi_dashboard: Update installiert (Backup unter {ziel_backup}), neue Version {version}")
