/**
 * Service, Werkstatt und Servicebuch. Vorlagen: `vService()`, `vWerkstatt()`,
 * `vSbuch()`.
 *
 * Die vom Fahrzeug gemeldete Fälligkeit und die aus dem Servicebuch
 * gerechnete Prognose stehen bewusst nebeneinander statt sich zu ersetzen:
 * Die Logik im Werkscomputer ist nicht bekannt und kann von den selbst
 * eingestellten Intervallen abweichen.
 */

import { useRef, useState } from "react"

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

import { useDaten } from "../daten/DatenKontext"
import {
  inspektionPrognose,
  letzterInspektion,
  letzterOelwechsel,
  meldungsPrognose,
  oelMeldungsPrognose,
  oelwechselPrognose,
} from "../daten/service"
import { datum, de, isoTag } from "../format"
import type { SeitenName } from "../navigation"
import { SymbolInspektion, SymbolOelwechsel } from "../symbole"
import { Leerzustand, NaviKachel, Wertzeile, Werteliste, bestaetigen } from "./bausteine"
import { kalenderDateiLaden } from "./kalender"
import { vcardParsen } from "./vcard"

export interface ServicebuchEintrag {
  datum?: string
  km?: number
  art?: string
  betrieb?: string
  notiz?: string
  [weitere: string]: unknown
}

interface Autohaus {
  name?: string
  adresse?: string
  tel?: string
  mail?: string
}

export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) => void }) {
  const { fahrzeug, fahrten } = useDaten()
  if (!fahrzeug) return null

  const buch = (fahrzeug.service["buch"] ?? []) as ServicebuchEintrag[]
  const autohaus = (fahrzeug.service["autohaus"] ?? {}) as Autohaus
  const prognose = oelwechselPrognose(fahrzeug, buch)
  const inspektionsprognose = inspektionPrognose(fahrzeug, buch)

  // Ölwechsel und Inspektion je einzeln, statt einer gemeinsamen Wertzeilen-
  // Liste unter "Nächster Ölwechsel" (die Inspektion/Hauptuntersuchung dort
  // fachfremd mitschleppte) - deckungsgleich mit vService() im Panel: drei
  // sichtbar getrennte Blöcke, jeder mit eigenem Icon, optionaler
  // Herstellervorgabe-Zeile und optionaler Prognose-Fußzeile.
  const oelHatEintrag = letzterOelwechsel(buch) != null
  const oelHatMeldung = fahrzeug.oelwechselFaelligTs != null || fahrzeug.oelwechselFaelligKm != null
  const oelWert = oelHatMeldung
    ? [
        fahrzeug.oelwechselFaelligKm != null ? `${de(Math.abs(fahrzeug.oelwechselFaelligKm))} km` : null,
        fahrzeug.oelwechselFaelligTs ? datum(fahrzeug.oelwechselFaelligTs) : null,
      ]
        .filter(Boolean)
        .join(" / ")
    : prognose
      ? `${de(prognose.restKm)} km · ${datum(prognose.datum)}`
      : null
  // Ganz ohne Servicebucheintrag (prognose null) übernimmt die
  // Fahrzeugmeldung selbst die Rolle der Prognosequelle - bei Herstellervorgabe
  // unverändert, bei eigenem (kürzerem) Intervall über
  // oelMeldungsPrognose()/meldungsPrognoseEigenesIntervall() auf das eigene
  // Intervall hochgerechnet, statt es bis zum ersten Servicebucheintrag zu
  // ignorieren (siehe deren Kommentare in service.ts).
  const oelMeldungProg = oelHatMeldung && !prognose ? oelMeldungsPrognose(fahrzeug, fahrten) : null
  // Bei Herstellervorgabe rechnet die eigene Prognose ohnehin schon mit dem
  // Herstellerintervall - die Restkilometerzahl daneben wäre nur eine zweite,
  // verwirrende km-Angabe neben dem Fahrzeug-Wert oben. Nur beim eigenen
  // (kürzeren) Intervall ist sie eine echte Zusatzinformation.
  const oelFussnote = oelHatMeldung && prognose
    ? fahrzeug.oel.modus === "hersteller"
      ? `voraussichtlich am ${datum(prognose.datum)}`
      : `${de(prognose.restKm)} km, voraussichtlich am ${datum(prognose.datum)}`
    : oelMeldungProg
      ? fahrzeug.oel.modus === "hersteller" || oelMeldungProg.restKm == null
        ? `voraussichtlich am ${datum(oelMeldungProg.datum)}`
        : `${de(oelMeldungProg.restKm)} km, voraussichtlich am ${datum(oelMeldungProg.datum)}`
      : null
  // Der Ölwechsel folgt in "Einrichten" wahlweise der Herstellervorgabe oder
  // einem kürzeren eigenen Intervall (fahrzeug.oel.modus) - die Inspektion hat
  // kein einstellbares Intervall, rechnet also immer mit der Herstellervorgabe
  // (siehe inspektionPrognose()'s eigener Kommentar dazu). Eine Fahrzeugmeldung
  // stammt aber immer aus dem werkseigenen Wartungsprogramm des Bordcomputers -
  // der kennt das eigene App-Intervall gar nicht -, zählt also unabhängig vom
  // gewählten Modus als Herstellervorgabe.
  const oelHerstellervorgabe = oelHatMeldung || fahrzeug.oel.modus === "hersteller"

  const inspHatEintrag = letzterInspektion(buch) != null
  const inspHatMeldung = fahrzeug.inspektionFaelligTs != null || fahrzeug.inspektionFaelligKm != null
  const inspWert = inspHatMeldung
    ? [
        fahrzeug.inspektionFaelligKm != null ? `${de(Math.abs(fahrzeug.inspektionFaelligKm))} km` : null,
        fahrzeug.inspektionFaelligTs ? datum(fahrzeug.inspektionFaelligTs) : null,
      ]
        .filter(Boolean)
        .join(" / ")
    : inspektionsprognose
      ? datum(inspektionsprognose.datum)
      : null
  const inspMeldungProg = inspHatMeldung && !inspektionsprognose
    ? meldungsPrognose(fahrzeug.inspektionFaelligTs, fahrzeug.inspektionFaelligKm, fahrten)
    : null
  const inspFussnote = inspektionsprognose
    ? `voraussichtlich am ${datum(inspektionsprognose.datum)}`
    : inspMeldungProg
      ? `voraussichtlich am ${datum(inspMeldungProg.datum)}`
      : null

  const huDatum = fahrzeug.hu ? new Date(fahrzeug.hu) : null
  const huGueltig = !!huDatum && !Number.isNaN(huDatum.getTime())

  return (
    <>
      <Tile>
        <span className="ads-eyebrow">Anstehende Termine</span>

        <div className="dm-termingruppe">
          <div className="dm-tgkopf">
            <SymbolOelwechsel groesse={26} />
            <span>Ölwechsel</span>
          </div>
          {oelHatEintrag || oelHatMeldung ? (
            <>
              {oelHerstellervorgabe && <div className="dm-tgvorgabe">Herstellervorgabe</div>}
              <div className="dm-tgwert">{oelWert}</div>
              {oelFussnote && <div className="dm-tgprognose">{oelFussnote}</div>}
            </>
          ) : (
            <div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
          )}
        </div>

        <div className="dm-termingruppe">
          <div className="dm-tgkopf">
            <SymbolInspektion groesse={20} />
            <span>Inspektion</span>
          </div>
          {inspHatEintrag || inspHatMeldung ? (
            <>
              <div className="dm-tgvorgabe">Herstellervorgabe</div>
              <div className="dm-tgwert">{inspWert}</div>
              {inspFussnote && <div className="dm-tgprognose">{inspFussnote}</div>}
            </>
          ) : (
            <div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
          )}
        </div>

        <div className="dm-termingruppe">
          <div className="dm-tgkopf">
            <span>Hauptuntersuchung</span>
          </div>
          {huGueltig ? (
            <div className="dm-tgwert">{datum(fahrzeug.hu)}</div>
          ) : (
            <div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
          )}
        </div>

        {prognose && (
          <div style={{ marginTop: 14 }}>
            <ActionButton
              onClick={() =>
                kalenderDateiLaden("Ölwechsel", prognose.datum, "Ölwechsel laut eigener Prognose")
              }
            >
              In den Kalender eintragen
            </ActionButton>
          </div>
        )}
        <p className="dm-fussnote">
          Die eigene Prognose rechnet ab dem letzten Eintrag der jeweiligen Art im Servicebuch neu
          und nutzt die seither tatsächlich gefahrene Strecke. Sie ersetzt die Meldung des
          Fahrzeugs nicht, sondern ergänzt sie.
        </p>
      </Tile>

      <div className="dm-raster">
        <NaviKachel
          titel="Werkstatt"
          wert={autohaus.name || "Noch nicht hinterlegt"}
          beiKlick={() => geheZu("werkstatt")}
        />
        <NaviKachel
          titel="Servicebuch"
          wert={`${de(buch.length)} Eintrag${buch.length === 1 ? "" : "e"}`}
          beiKlick={() => geheZu("sbuch")}
        />
      </div>
    </>
  )
}

export function Werkstatt() {
  const { fahrzeug, profilSpeichern } = useDaten()
  const autohausRoh = (fahrzeug?.service["autohaus"] ?? {}) as Autohaus
  const [entwurf, setzeEntwurf] = useState<Autohaus>({ ...autohausRoh })
  const [laeuft, setzeLaeuft] = useState(false)
  const [gespeichert, setzeGespeichert] = useState(false)
  const [vcardFehler, setzeVcardFehler] = useState<string | null>(null)
  const vcardDatei = useRef<HTMLInputElement | null>(null)

  if (!fahrzeug) return null

  const speichern = async () => {
    setzeLaeuft(true)
    setzeGespeichert(false)
    try {
      const neu = { ...fahrzeug, service: { ...fahrzeug.service, autohaus: entwurf } }
      await profilSpeichern({ fahrzeug: neu })
      setzeGespeichert(true)
    } finally {
      setzeLaeuft(false)
    }
  }

  // Befüllt nur den Entwurf, speichert nicht selbst - der Nutzer prüft die
  // übernommenen Felder wie bei jeder anderen Änderung erst im Formular,
  // bevor "Speichern" etwas an Home Assistant schickt. Leere Felder aus der
  // vCard überschreiben vorhandene Werte im Entwurf nicht.
  const vcardImportieren = (datei: File) => {
    setzeVcardFehler(null)
    setzeGespeichert(false)
    const leser = new FileReader()
    leser.onload = () => {
      try {
        const felder = vcardParsen(String(leser.result))
        setzeEntwurf((alt) => ({
          ...alt,
          ...(felder.name ? { name: felder.name } : {}),
          ...(felder.adresse ? { adresse: felder.adresse } : {}),
          ...(felder.tel ? { tel: felder.tel } : {}),
          ...(felder.mail ? { mail: felder.mail } : {}),
        }))
      } catch (fehler) {
        setzeVcardFehler(
          fehler instanceof Error ? fehler.message : "Die vCard ließ sich nicht lesen.",
        )
      }
    }
    leser.readAsText(datei)
  }

  const feld = (schluessel: keyof Autohaus, beschriftung: string, letztes = false) => (
    <Feld label={beschriftung} last={letztes}>
      <input
        className="dm-eingabe"
        value={entwurf[schluessel] ?? ""}
        onChange={(e) => setzeEntwurf((alt) => ({ ...alt, [schluessel]: e.target.value }))}
      />
    </Feld>
  )

  return (
    <Tile>
      <span className="ads-eyebrow">Werkstatt</span>
      {feld("name", "Name")}
      {feld("adresse", "Adresse")}
      {feld("tel", "Telefon")}
      {feld("mail", "E-Mail", true)}
      {vcardFehler && <p className="dm-fehler">{vcardFehler}</p>}
      {gespeichert && <p className="dm-erfolg">Gespeichert.</p>}
      <div className="dm-knopfreihe" style={{ marginTop: 14 }}>
        <ActionButton onClick={() => void speichern()} disabled={laeuft}>
          {laeuft ? "Speichere …" : "Speichern"}
        </ActionButton>
        <ActionButton onClick={() => vcardDatei.current?.click()}>vCard importieren</ActionButton>
      </div>
      <input
        ref={vcardDatei}
        type="file"
        accept=".vcf,text/vcard"
        hidden
        onChange={(e) => {
          const datei = e.target.files?.[0]
          if (datei) vcardImportieren(datei)
          e.target.value = ""
        }}
      />
    </Tile>
  )
}

export function Servicebuch() {
  const { fahrzeug, profilSpeichern } = useDaten()
  const [neuOffen, setzeNeuOffen] = useState(false)
  const [entwurf, setzeEntwurf] = useState<ServicebuchEintrag>({ datum: isoTag(), art: "Ölwechsel" })
  const [laeuft, setzeLaeuft] = useState(false)

  if (!fahrzeug) return null
  const buch = (fahrzeug.service["buch"] ?? []) as ServicebuchEintrag[]

  const schreiben = async (neuesBuch: ServicebuchEintrag[]) => {
    setzeLaeuft(true)
    try {
      await profilSpeichern({
        fahrzeug: { ...fahrzeug, service: { ...fahrzeug.service, buch: neuesBuch } },
      })
    } finally {
      setzeLaeuft(false)
    }
  }

  const hinzufuegen = async () => {
    await schreiben(
      [...buch, entwurf].sort((a, b) => String(b.datum ?? "").localeCompare(String(a.datum ?? ""))),
    )
    setzeNeuOffen(false)
    setzeEntwurf({ datum: isoTag(), art: "Ölwechsel" })
  }

  const entfernen = async (index: number) => {
    if (!bestaetigen("Diesen Servicebuch-Eintrag löschen?")) return
    await schreiben(buch.filter((_, i) => i !== index))
  }

  return (
    <>
      <div className="dm-abschnitt">
        <span className="ads-eyebrow">{de(buch.length)} Einträge</span>
        <button
          type="button"
          className="dm-textknopf"
          onClick={() => setzeNeuOffen((offen) => !offen)}
        >
          {neuOffen ? "Abbrechen" : "Eintrag hinzufügen"}
        </button>
      </div>

      {neuOffen && (
        <Tile>
          <span className="ads-eyebrow">Neuer Eintrag</span>
          <Feld label="Datum">
            <input
              className="dm-eingabe"
              type="date"
              value={entwurf.datum ?? ""}
              onChange={(e) => setzeEntwurf((alt) => ({ ...alt, datum: e.target.value }))}
            />
          </Feld>
          <Feld label="Art">
            <select
              className="dm-auswahl"
              value={entwurf.art ?? "Ölwechsel"}
              onChange={(e) => setzeEntwurf((alt) => ({ ...alt, art: e.target.value }))}
            >
              <option>Ölwechsel</option>
              <option>Inspektion</option>
              <option>Hauptuntersuchung</option>
              <option>Reparatur</option>
              <option>Sonstiges</option>
            </select>
          </Feld>
          <Feld label="Kilometerstand" unit="km">
            <input
              className="dm-eingabe"
              inputMode="numeric"
              value={entwurf.km ?? ""}
              onChange={(e) => setzeEntwurf((alt) => ({ ...alt, km: Number(e.target.value) }))}
            />
          </Feld>
          <Feld label="Betrieb">
            <input
              className="dm-eingabe"
              value={entwurf.betrieb ?? ""}
              onChange={(e) => setzeEntwurf((alt) => ({ ...alt, betrieb: e.target.value }))}
            />
          </Feld>
          <Feld label="Notiz" last>
            <input
              className="dm-eingabe"
              value={entwurf.notiz ?? ""}
              onChange={(e) => setzeEntwurf((alt) => ({ ...alt, notiz: e.target.value }))}
            />
          </Feld>
          <div style={{ marginTop: 14 }}>
            <ActionButton onClick={() => void hinzufuegen()} disabled={laeuft}>
              Speichern
            </ActionButton>
          </div>
        </Tile>
      )}

      {buch.length === 0 && !neuOffen && (
        <Leerzustand
          titel="Servicebuch ist leer"
          text="Trag hier ein, was am Fahrzeug gemacht wurde. Aus den Ölwechseln entsteht die eigene Wartungsprognose."
          aktion={{ beschriftung: "Eintrag hinzufügen", beiKlick: () => setzeNeuOffen(true) }}
        />
      )}

      {buch.map((eintrag, index) => (
        <Tile key={`${eintrag.datum}-${index}`}>
          <div className="dm-detail__kopf">
            <div>
              <span className="ads-eyebrow">{eintrag.datum ? datum(eintrag.datum) : "ohne Datum"}</span>
              <div style={{ fontSize: 17, fontWeight: 300, marginTop: 4 }}>
                {eintrag.art ?? "Eintrag"}
              </div>
            </div>
            <button
              type="button"
              className="dm-textknopf"
              onClick={() => void entfernen(index)}
              disabled={laeuft}
            >
              Löschen
            </button>
          </div>
          <Werteliste
            kinder={
              <>
                <Wertzeile
                  label="Kilometerstand"
                  wert={eintrag.km != null ? `${de(eintrag.km)} km` : "—"}
                />
                <Wertzeile label="Betrieb" wert={eintrag.betrieb || "—"} />
                {eintrag.notiz && <Wertzeile label="Notiz" wert={eintrag.notiz} />}
              </>
            }
          />
        </Tile>
      ))}
    </>
  )
}
