/**
 * Einstellungen. Vorlage: `vEinst()` im alten Panel — die umfangreichste
 * Seite: Fahrzeugdaten, Darstellung, Fotos, SmartDeal, Pausenzeit, Sicherung,
 * Datenausgabe und Zugang.
 */

import { useEffect, useRef, useState } from "react"

import { ActionButton, Feld, Seg, Switch, Tile } from "@audi-dash/ui"

import { DIENST_DOMAIN, zugangLesen, zugangVerwerfen } from "../api"
import { eigeneVersion } from "../daten/appVersion"
import { useDaten } from "../daten/DatenKontext"
import { OtaFehler, buendelAnwenden, buendelPasst, otaMoeglich } from "../daten/ota"
import type { Einstellungen as EinstellungenWerte } from "../daten/profilAdapter"
import { datumZeit, de, isoTag } from "../format"
import { useTheme } from "../theme"
import { Wertzeile, Werteliste, bestaetigen } from "./bausteine"
import { Bild } from "./Bild"
import { BILDPLAETZE } from "./bilder"
import { csvHerunterladen } from "./csv"
import { HistorienImport } from "./HistorienImport"
import { zugangVergessen } from "./zugang"

export function Einstellungen({
  tabBeschriftung,
  setzeTabBeschriftung,
  beiAbmeldung,
}: {
  tabBeschriftung: boolean
  setzeTabBeschriftung: (an: boolean) => void
  beiAbmeldung: () => void
}) {
  const {
    einstellungen,
    fahrzeug,
    fahrten,
    tankvorgaenge,
    rohprofil,
    otaBuendel,
    integrationUpdate,
    verbindung,
    api,
    profilSpeichern,
    jetztAktualisieren,
    neuLaden,
  } = useDaten()
  const [theme, themeSetzen] = useTheme()
  const [entwurf, setzeEntwurf] = useState<EinstellungenWerte | null>(null)
  const [laeuft, setzeLaeuft] = useState(false)
  const [gespeichert, setzeGespeichert] = useState(false)
  const [importOffen, setzeImportOffen] = useState(false)
  const [otaLaeuft, setzeOtaLaeuft] = useState(false)
  const [otaFehler, setzeOtaFehler] = useState<string | null>(null)
  const [updatePruefenLaeuft, setzeUpdatePruefenLaeuft] = useState(false)
  const [updateInstallierenLaeuft, setzeUpdateInstallierenLaeuft] = useState(false)
  const [updateNeustartLaeuft, setzeUpdateNeustartLaeuft] = useState(false)
  const [updateAktionFehler, setzeUpdateAktionFehler] = useState<string | null>(null)
  const dateiwahl = useRef<HTMLInputElement | null>(null)

  const otaUpdateVerfuegbar = otaMoeglich() && buendelPasst(otaBuendel, eigeneVersion())

  const otaAusloesen = async () => {
    if (!otaBuendel) return
    setzeOtaLaeuft(true)
    setzeOtaFehler(null)
    try {
      const zugang = await zugangLesen()
      if (!zugang) throw new OtaFehler("Kein Zugang eingerichtet.")
      // Kehrt im Erfolgsfall nicht zurück - set() lädt die App neu, siehe
      // daten/ota.ts. Der catch-Block unten fängt sowohl echte Fehlschläge
      // als auch den defensiven letzten throw in buendelAnwenden() ab.
      await buendelAnwenden(otaBuendel, zugang.basisUrl)
    } catch (fehler) {
      setzeOtaFehler(
        fehler instanceof Error ? fehler.message : "Das Update konnte nicht installiert werden.",
      )
      setzeOtaLaeuft(false)
    }
  }

  // Selbst-Update der Integration (aktualisierung.py) - Ersatz für
  // install.ps1, das Windows Smart App Control blockiert. Beide Aktionen
  // laufen sofort (keine Warteschlange, siehe api/index.ts) und laden
  // danach über neuLaden() den neuen Stand von
  // sensor.audi_dashboard_app_version - der Fehlertext bei fehlendem Token
  // kommt von dort mit, eine eigene "ist ein Token eingetragen?"-Prüfung
  // gibt es bewusst nicht. updateAktionFehler fängt zusätzlich echte
  // Verbindungsfehler ab (dienste.py behandelt AktualisierungsFehler selbst
  // und liefert 200 zurück - hier landet nur, was gar nicht erst bei Home
  // Assistant ankam), sonst bliebe ein solcher Fehlschlag ganz ohne
  // Rückmeldung.
  const updatePruefenAusloesen = async () => {
    setzeUpdatePruefenLaeuft(true)
    setzeUpdateAktionFehler(null)
    try {
      await api.updatePruefen()
      await neuLaden()
    } catch (fehler) {
      setzeUpdateAktionFehler(
        fehler instanceof Error ? fehler.message : "Die Prüfung konnte nicht ausgeführt werden.",
      )
    } finally {
      setzeUpdatePruefenLaeuft(false)
    }
  }

  const updateInstallierenAusloesen = async () => {
    setzeUpdateInstallierenLaeuft(true)
    setzeUpdateAktionFehler(null)
    try {
      await api.updateInstallieren()
      await neuLaden()
    } catch (fehler) {
      setzeUpdateAktionFehler(
        fehler instanceof Error ? fehler.message : "Das Update konnte nicht installiert werden.",
      )
    } finally {
      setzeUpdateInstallierenLaeuft(false)
    }
  }

  // Nach einer erfolgreichen Installation zeigt die Kachel direkt den
  // nächsten nötigen Schritt statt den Nutzer selbst suchen zu lassen - ein
  // reiner Reload reicht nicht, siehe AGENTS.md Abschnitt J. Kein try/finally
  // um setzeUpdateNeustartLaeuft: bricht die Verbindung normalerweise durch
  // den Neustart selbst ab, bevor der Aufruf überhaupt zurückkehrt.
  const updateNeustartAusloesen = async () => {
    setzeUpdateNeustartLaeuft(true)
    setzeUpdateAktionFehler(null)
    try {
      await api.homeAssistantNeuStarten()
    } catch (fehler) {
      setzeUpdateAktionFehler(
        fehler instanceof Error
          ? fehler.message
          : "Home Assistant konnte nicht neu gestartet werden.",
      )
      setzeUpdateNeustartLaeuft(false)
    }
  }

  // Ergänzt das absichtliche Fehlen des try/finally oben: die Verbindung
  // trennt sich für die Dauer des HA-Neustarts, DatenKontext meldet das über
  // `verbindung`/`api.live.aufVerbindung()` und lädt bei "verbunden" bereits
  // integrationUpdate & Co. neu (siehe DatenKontext.tsx) - aber
  // updateNeustartLaeuft ist reiner Bildschirm-Zustand hier und blieb davon
  // unberührt. Ohne diesen Effekt stand "Home Assistant startet neu …" nach
  // einem erfolgreichen Neustart weiter da, bis die Seite von Hand neu
  // geladen wurde.
  useEffect(() => {
    if (verbindung === "verbunden" && updateNeustartLaeuft) setzeUpdateNeustartLaeuft(false)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [verbindung])

  if (!einstellungen || !fahrzeug) return null
  const werte = entwurf ?? einstellungen

  const aendern = (teil: Partial<EinstellungenWerte>) =>
    setzeEntwurf({ ...werte, ...teil })

  const speichern = async () => {
    if (!entwurf) return
    setzeLaeuft(true)
    setzeGespeichert(false)
    try {
      await profilSpeichern({ einstellungen: entwurf })
      setzeEntwurf(null)
      setzeGespeichert(true)
    } finally {
      setzeLaeuft(false)
    }
  }

  const abmelden = async () => {
    if (!bestaetigen("Zugang von diesem Gerät entfernen? Die Daten auf dem Server bleiben.")) return
    await zugangVerwerfen()
    zugangVergessen()
    beiAbmeldung()
  }

  const profilExportieren = () => {
    const inhalt = JSON.stringify(
      { profil: rohprofil, fahrten, tankvorgaenge, erzeugt: new Date().toISOString() },
      null,
      2,
    )
    const blob = new Blob([inhalt], { type: "application/json" })
    const url = URL.createObjectURL(blob)
    const verweis = document.createElement("a")
    verweis.href = url
    verweis.download = `datametric360-sicherung-${isoTag()}.json`
    verweis.click()
    URL.revokeObjectURL(url)
  }

  return (
    <>
      <Tile>
        <span className="ads-eyebrow">Fahrzeug</span>
        <Feld label="Modell">
          <input
            className="dm-eingabe"
            value={werte.fahrzeugtitel}
            onChange={(e) => aendern({ fahrzeugtitel: e.target.value })}
          />
        </Feld>
        <Feld label="Zusatz">
          <input
            className="dm-eingabe"
            value={werte.fahrzeugzusatz}
            onChange={(e) => aendern({ fahrzeugzusatz: e.target.value })}
          />
        </Feld>
        <Feld label="Kennzeichen">
          <input
            className="dm-eingabe"
            value={werte.kennzeichen}
            onChange={(e) => aendern({ kennzeichen: e.target.value })}
          />
        </Feld>
        <Feld label="Tankvolumen" unit="l" last>
          <input
            className="dm-eingabe"
            inputMode="numeric"
            value={werte.tankvolumen}
            onChange={(e) => aendern({ tankvolumen: Number(e.target.value) || 0 })}
          />
        </Feld>
      </Tile>

      <Tile>
        <span className="ads-eyebrow">Fahrten</span>
        <Feld label="Pause zwischen zwei Fahrten" unit="Minuten">
          <input
            className="dm-eingabe"
            inputMode="numeric"
            value={werte.pauseMin}
            onChange={(e) => aendern({ pauseMin: Number(e.target.value) || 0 })}
          />
        </Feld>
        <Feld label="Nacht beginnt">
          <input
            className="dm-eingabe"
            type="time"
            value={werte.nachtVon}
            onChange={(e) => aendern({ nachtVon: e.target.value })}
          />
        </Feld>
        <Feld label="Nacht endet" last>
          <input
            className="dm-eingabe"
            type="time"
            value={werte.nachtBis}
            onChange={(e) => aendern({ nachtBis: e.target.value })}
          />
        </Feld>
        <p className="dm-fussnote">
          Setzt das Fahrzeug innerhalb der Pausenzeit die Fahrt fort, zählt das als eine Fahrt und
          nicht als zwei — etwa bei einem Tankstopp.
        </p>
      </Tile>

      <Tile>
        <span className="ads-eyebrow">Darstellung</span>
        <Feld label="Erscheinungsbild">
          <Seg
            options={[
              { value: "nacht", label: "Nacht" },
              { value: "tag", label: "Tag" },
            ]}
            value={theme}
            onChange={(neu) => themeSetzen(neu as "nacht" | "tag")}
          />
        </Feld>
        <Feld label="Beschriftung der Tab-Leiste" last>
          <Switch
            checked={tabBeschriftung}
            onChange={setzeTabBeschriftung}
            aria-label="Beschriftung der Tab-Leiste"
          />
        </Feld>
      </Tile>

      <Tile>
        <span className="ads-eyebrow">Bild auf der Übersicht</span>
        <Feld label="Verwendetes Bild" last>
          <select
            className="dm-auswahl"
            value={werte.startbild}
            onChange={(e) => aendern({ startbild: e.target.value })}
          >
            {BILDPLAETZE.map((platz) => (
              <option key={platz.datei} value={platz.datei}>
                {platz.name}
              </option>
            ))}
            <option value="seitenansicht-winter.webp">Seitenansicht Winter</option>
          </select>
        </Feld>
        <div className="dm-galerie">
          {BILDPLAETZE.map((platz) => (
            <Bild
              key={platz.datei}
              datei={platz.datei}
              beschreibung={platz.beschreibung}
              name={platz.name}
              groesse="mini"
            />
          ))}
        </div>
        <p className="dm-fussnote">
          Fotos lädst du über die Verwaltung im Home Assistant hoch. Die Dateinamen sind fest
          vergeben — der Server nimmt nur diese an.
        </p>
      </Tile>

      <Tile>
        <span className="ads-eyebrow">Tankrabatt</span>
        <Feld label={`${werte.markenname}-Rabatt aktiv`}>
          <Switch
            checked={fahrzeug.smartdeal.aktiv}
            onChange={(an) =>
              void profilSpeichern({
                fahrzeug: {
                  ...fahrzeug,
                  smartdeal: { ...fahrzeug.smartdeal, aktiv: an },
                },
              })
            }
            aria-label="Tankrabatt aktiv"
          />
        </Feld>
        <Feld label="Gültig bis" last>
          <input
            className="dm-eingabe"
            type="date"
            value={fahrzeug.smartdeal.laeuftAb ?? ""}
            onChange={(e) =>
              void profilSpeichern({
                fahrzeug: {
                  ...fahrzeug,
                  smartdeal: { ...fahrzeug.smartdeal, laeuftAb: e.target.value || null },
                },
              })
            }
          />
        </Feld>
        <p className="dm-fussnote">
          Das Datum gilt einschließlich — am Stichtag selbst zählt der Rabatt noch, erst am Folgetag
          schaltet er ab.
        </p>
      </Tile>

      {/* Nachträglicher Import aus dem HA-Verlauf. Steht bewusst vor
          „Sicherung": beides holt Daten von außen in die App, und der Import
          ist der Schritt, den man einmal nach der Einrichtung braucht. */}
      <Tile>
        <span className="ads-eyebrow">Vergangene Daten</span>
        <p className="dm-fussnote">
          Legt Fahrten, Tankvorgänge und Spannungswerte für einen vergangenen Zeitraum
          nachträglich an — aus dem Verlauf, den Home Assistant zu den zugeordneten Sensoren
          bereits aufgezeichnet hat.
        </p>
        <div style={{ marginTop: 14 }}>
          <ActionButton onClick={() => setzeImportOffen(true)}>
            Daten importieren aus Home Assistant
          </ActionButton>
        </div>
      </Tile>

      <HistorienImport
        offen={importOffen}
        beiSchliessen={() => setzeImportOffen(false)}
        api={api}
        beiFertig={() => void jetztAktualisieren()}
      />

      <Tile>
        <span className="ads-eyebrow">Sicherung</span>
        <Feld label="Automatische Sicherung">
          <select
            className="dm-auswahl"
            value={werte.backupIntervall}
            onChange={(e) => aendern({ backupIntervall: e.target.value })}
          >
            <option value="aus">aus</option>
            <option value="taeglich">täglich</option>
            <option value="woechentlich">wöchentlich</option>
            <option value="monatlich">monatlich</option>
          </select>
        </Feld>
        <Werteliste
          kinder={
            <Wertzeile
              label="Zuletzt gesichert"
              wert={werte.letztesBackup ? datumZeit(werte.letztesBackup) : "—"}
            />
          }
        />
        <div className="dm-knopfreihe">
          <ActionButton
            onClick={() =>
              void api.rest.dienstAufrufen(DIENST_DOMAIN, "backup_jetzt")
            }
          >
            Jetzt sichern
          </ActionButton>
          <ActionButton onClick={profilExportieren}>Auf dieses Gerät laden</ActionButton>
        </div>
      </Tile>

      <Tile>
        <span className="ads-eyebrow">Daten ausgeben</span>
        <p className="dm-fussnote">
          Als CSV mit Semikolon und deutschem Zahlenformat — so öffnet Excel die Datei ohne
          Nachfragen.
        </p>
        <div className="dm-knopfreihe">
          <ActionButton
            onClick={() =>
              csvHerunterladen(
                "fahrten",
                ["Beginn", "Ende", "Strecke km", "Dauer s", "Art", "Status"],
                fahrten.map((f) => [
                  f.ts_start,
                  f.ts_end,
                  f.distance_km ?? "",
                  f.duration_s,
                  f.art,
                  f.status,
                ]),
              )
            }
          >
            Fahrten
          </ActionButton>
          <ActionButton
            onClick={() =>
              csvHerunterladen(
                "tankvorgaenge",
                ["Zeitpunkt", "Liter", "Betrag", "Preis je Liter", "Tankstelle", "Kilometerstand"],
                tankvorgaenge.map((t) => [
                  t.ts,
                  t.liters ?? "",
                  t.fuel_total_eur ?? "",
                  t.price_per_l ?? "",
                  t.station_name ?? "",
                  t.odometer_km ?? "",
                ]),
              )
            }
          >
            Tankvorgänge
          </ActionButton>
        </div>
      </Tile>

      {otaMoeglich() && (
        <Tile>
          <span className="ads-eyebrow">App-Update</span>
          {otaUpdateVerfuegbar && otaBuendel ? (
            <>
              <Werteliste
                kinder={
                  <Wertzeile
                    label="Verfügbare Version"
                    wert={otaBuendel.version}
                    zusatz={`${Math.round(otaBuendel.bytes / 1024)} KB`}
                  />
                }
              />
              <p className="dm-fussnote">
                Lädt die Oberfläche neu und startet die App neu. Fahrzeugprofil, Fahrten und
                Tankvorgänge sind davon nicht betroffen — die liegen in Home Assistant.
              </p>
              <div className="dm-knopfreihe">
                <ActionButton onClick={() => void otaAusloesen()} disabled={otaLaeuft}>
                  {otaLaeuft ? "Lade Update …" : "Update installieren"}
                </ActionButton>
              </div>
              {otaFehler && <p className="dm-fehler">{otaFehler}</p>}
            </>
          ) : (
            <p className="dm-fussnote">
              {otaBuendel
                ? `Diese App ist aktuell (${eigeneVersion() ?? "unbekannte Version"}).`
                : "Der Server hat gerade kein Update-Bündel hinterlegt."}
            </p>
          )}
        </Tile>
      )}

      <Tile>
        <span className="ads-eyebrow">Integration-Update</span>
        {/* HIG "Loading": Netzwerk zu Gitea plus Entpacken dauert unbestimmt
            lang, ohne verlässliche Fortschrittsangabe - ein laufender
            Indikator statt eines nur deaktivierten Knopfes verhindert, dass
            das wie ein Hänger aussieht. Nach Erfolg führt der Knopf direkt
            zum nächsten nötigen Schritt (Neustart) statt den Nutzer selbst
            suchen zu lassen - ein reiner Reload reicht nicht (AGENTS.md
            Abschnitt J). */}
        {updateInstallierenLaeuft ? (
          <div className="dm-ladezeile">
            <span className="dm-spinner" aria-hidden="true" />
            <span className="dm-fussnote" style={{ margin: 0 }}>
              Installiere …
            </span>
          </div>
        ) : updateNeustartLaeuft ? (
          <div className="dm-ladezeile">
            <span className="dm-spinner" aria-hidden="true" />
            <span className="dm-fussnote" style={{ margin: 0 }}>
              Home Assistant startet neu …
            </span>
          </div>
        ) : integrationUpdate?.installiert ? (
          <>
            <p className="dm-fussnote">
              Version {integrationUpdate.version} installiert - die neue Fassung wird erst nach
              einem Neustart geladen.
            </p>
            <div className="dm-knopfreihe">
              <ActionButton onClick={() => void updateNeustartAusloesen()}>
                Installation abschließen - Jetzt neu starten
              </ActionButton>
            </div>
          </>
        ) : integrationUpdate?.verfuegbar ? (
          <>
            <Werteliste
              kinder={<Wertzeile label="Verfügbare Version" wert={integrationUpdate.version ?? "?"} />}
            />
            <p className="dm-fussnote">
              Lädt die neueste Fassung von Gitea und ersetzt den Integrationsordner. Home Assistant
              muss danach neu gestartet werden. Fahrzeugprofil, Fahrten und Tankvorgänge sind davon
              nicht betroffen.
            </p>
            <div className="dm-knopfreihe">
              <ActionButton onClick={() => void updateInstallierenAusloesen()}>
                Update installieren
              </ActionButton>
            </div>
          </>
        ) : updatePruefenLaeuft ? (
          <div className="dm-ladezeile">
            <span className="dm-spinner" aria-hidden="true" />
            <span className="dm-fussnote" style={{ margin: 0 }}>
              Prüfe …
            </span>
          </div>
        ) : (
          <>
            <p className="dm-fussnote">
              {integrationUpdate
                ? `Integration ist aktuell (${integrationUpdate.geprueft_am ? datumZeit(integrationUpdate.geprueft_am) : ""}).`
                : "Noch nicht geprüft."}
            </p>
            <div className="dm-knopfreihe">
              <ActionButton onClick={() => void updatePruefenAusloesen()}>
                Auf Update prüfen
              </ActionButton>
            </div>
          </>
        )}
        {updateAktionFehler && <p className="dm-fehler">{updateAktionFehler}</p>}
        {!updateAktionFehler &&
          integrationUpdate?.fehler &&
          !updatePruefenLaeuft &&
          !updateInstallierenLaeuft &&
          !updateNeustartLaeuft && <p className="dm-fehler">{integrationUpdate.fehler}</p>}
      </Tile>

      <Tile>
        <span className="ads-eyebrow">Zugang</span>
        <Werteliste
          kinder={
            <>
              <Wertzeile label="Fahrten gespeichert" wert={de(fahrten.length)} />
              <Wertzeile label="Tankvorgänge gespeichert" wert={de(tankvorgaenge.length)} />
            </>
          }
        />
        <div style={{ marginTop: 14 }}>
          <ActionButton variant="destructive" onClick={() => void abmelden()}>
            Verbindung trennen
          </ActionButton>
        </div>
      </Tile>

      <input ref={dateiwahl} type="file" accept="application/json" hidden />

      {gespeichert && <p className="dm-erfolg">Gespeichert.</p>}
      {entwurf && (
        <div className="dm-speicherleiste">
          <ActionButton onClick={() => void speichern()} disabled={laeuft}>
            {laeuft ? "Speichere …" : "Änderungen speichern"}
          </ActionButton>
          <button type="button" className="dm-textknopf" onClick={() => setzeEntwurf(null)}>
            Verwerfen
          </button>
        </div>
      )}
    </>
  )
}
