/**
 * Batteriespannung im Verlauf. Vorlage: `vBatterieverlauf()`.
 *
 * Das Diagramm ist von Hand als SVG gezeichnet — wie im alten Panel und aus
 * demselben Grund: eine Diagrammbibliothek wäre um ein Vielfaches größer als
 * die ganze Seite.
 *
 * Wichtig für die Einordnung: Der Ladezustand wird aus der **Tagesminimum**-
 * Spannung geschätzt (Ruhespannung kurz nach dem Start, bevor die Lichtmaschine
 * anhebt); Werte über 13,0 V gelten als „während der Fahrt gemessen" und
 * taugen nicht als Ruhewert.
 */

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

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

import { ENTITAETEN } from "../api"
import { useDaten } from "../daten/DatenKontext"
import { datum, de } from "../format"
import type { SeitenName } from "../navigation"
import { SymbolListe } from "../symbole"
import { Leerzustand, Wertzeile, Werteliste } from "./bausteine"

interface Tageswert {
  datum: string
  min: number | null
  max: number | null
  min_ts?: string
  max_ts?: string
  min_temp_c?: number | null
}

/** Ab hier gilt eine Messung nicht mehr als Batterie(ruhe)spannung, sondern
    schon als Generatorspannung - vom Nutzer direkt festgelegt ("max voltage
    for AGM Battery is 13V, everything above is the generator"), identisch
    mit AGM_RUHE_MAX_V im Panel (audi-dashboard-app.js). Getrennt von
    LADEKURVE unten: eine Messung zwischen 12,8 und 13,0 V zählt hier noch als
    gültige Batteriemessung (Diagrammpunkt, fließt in den Trend ein), zeigt
    aber weiterhin 100 % Ladezustand ("12,8v voll was already right" - der
    Nutzer bestätigte den ursprünglichen 12,8-V-Eckwert ausdrücklich). Auch
    von BatterieListe.tsx verwendet, für die "Generatorspannung"-
    Kennzeichnung dort - eine einzige Schwelle für beide Ansichten. */
export const AGM_RUHE_MAX_V = 13.0

/** Spannung → Ladezustand für eine AGM-Batterie in Ruhe, linear zwischen den
    Stützstellen interpoliert - identische Tabelle und Rechenweg wie im Panel
    (`bvSocProzent()`/`BV_SOC_TABELLE`, `audi-dashboard-app.js`), auf
    Nutzerwunsch angeglichen: die vorherige Stufenkurve hier lieferte bei
    derselben Spannung teils deutlich andere Prozentwerte als das Panel (z. B.
    12,1 V: 40 % im Panel, 25 % hier) - beide Ansichten sollen bei derselben
    Messung dasselbe zeigen. Letzte Stützstelle bleibt bei 12,8 V/100 % -
    unabhängig von der oben auf 13,0 V angehobenen AGM_RUHE_MAX_V, siehe
    deren eigenen Kommentar. */
const LADEKURVE: [number, number][] = [
  [10.5, 0], [11.8, 10], [11.9, 20], [12.0, 30], [12.1, 40],
  [12.2, 50], [12.4, 60], [12.5, 70], [12.6, 80], [12.7, 90], [12.8, 100],
]

function ladezustand(spannung: number): number | null {
  if (spannung > AGM_RUHE_MAX_V) return null
  const t = LADEKURVE
  if (spannung <= t[0]![0]) return 0
  if (spannung >= t[t.length - 1]![0]) return 100
  for (let i = 0; i < t.length - 1; i++) {
    const [v0, s0] = t[i]!
    const [v1, s1] = t[i + 1]!
    if (spannung >= v0 && spannung <= v1) return Math.round(s0 + ((s1 - s0) * (spannung - v0)) / (v1 - v0))
  }
  return 100
}

export function Batterie({ geheZu }: { geheZu: (name: SeitenName) => void }) {
  const { api, fahrzeug } = useDaten()
  const [verlauf, setzeVerlauf] = useState<Tageswert[] | null>(null)
  const [fehler, setzeFehler] = useState(false)

  useEffect(() => {
    void api.rest
      .datenLesen<Tageswert[]>(ENTITAETEN.batterieverlauf)
      .then((daten) => setzeVerlauf(Array.isArray(daten) ? daten : []))
      .catch(() => setzeFehler(true))
  }, [api])

  if (fehler) {
    return (
      <Leerzustand
        titel="Verlauf nicht abrufbar"
        text="Der Server hat den Batterieverlauf nicht geliefert. Beim nächsten Aufruf klappt es vielleicht wieder."
      />
    )
  }

  if (!verlauf) return <div className="dm-laden">Verlauf wird geladen …</div>

  // Wie bvPunkte im Panel: Werte über AGM_RUHE_MAX_V sind Generatorspannung,
  // keine Ruhespannung - ausgeschlossen von Kopfzeile, Trend UND Diagramm
  // (eine einzige gefilterte Liste, kein separater "alle Messungen"-Bestand).
  // Ohne diesen Filter hätte ein Tag, an dem das Fahrzeug nie lange genug
  // stand, seine Generatorspannung fälschlich als "Ruhespannung" gezeigt.
  const messwerte = verlauf.filter((t) => t.min != null && t.min <= AGM_RUHE_MAX_V)

  if (messwerte.length === 0) {
    return (
      <Leerzustand
        titel="Keine Messwerte"
        text="Für dieses Fahrzeug liefert die Anbindung derzeit keinen Spannungssensor. Sobald einer verfügbar ist, zeichnet die App den Verlauf hier auf."
      />
    )
  }

  const letzter = messwerte[messwerte.length - 1]!
  const soc = letzter.min != null ? ladezustand(letzter.min) : null

  // Trend als Vergleich früher gegen später — keine echte Kapazitätsmessung.
  const haelfte = Math.floor(messwerte.length / 2)
  const mittel = (liste: Tageswert[]) =>
    liste.length > 0 ? liste.reduce((s, t) => s + (t.min ?? 0), 0) / liste.length : null
  const frueher = mittel(messwerte.slice(0, haelfte))
  const spaeter = mittel(messwerte.slice(haelfte))
  const trend = frueher != null && spaeter != null ? spaeter - frueher : null

  return (
    <>
      <Tile>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <span className="ads-eyebrow">Ruhespannung</span>
          <IconButton aria-label="Messwertliste" onClick={() => geheZu("battliste")}>
            <SymbolListe groesse={18} />
          </IconButton>
        </div>
        <div className="dm-detail__kopf">
          <div className="dm-detail__zahl">
            {letzter.min != null ? de(letzter.min, 2) : "—"}
            <span className="dm-detail__einheit">V</span>
          </div>
        </div>
        <Verlaufsdiagramm werte={messwerte} />
        <Werteliste
          kinder={
            <>
              <Wertzeile label="Letzte Messung" wert={datum(letzter.datum)} />
              <Wertzeile
                label="Geschätzter Ladezustand"
                wert={soc != null ? `${de(soc)} %` : "—"}
                zusatz={soc == null ? "Messung nicht in Ruhe aufgenommen" : undefined}
              />
              <Wertzeile
                label="Trend über den Zeitraum"
                wert={trend != null ? `${trend >= 0 ? "+" : ""}${de(trend, 2)} V` : "—"}
              />
              <Wertzeile
                label="Aktuell gemeldet"
                wert={
                  fahrzeug?.batteriespannung != null
                    ? `${de(fahrzeug.batteriespannung, 2)} V`
                    : "—"
                }
              />
            </>
          }
        />
        <p className="dm-fussnote">
          Der Ladezustand wird aus dem Tagesminimum geschätzt — das ist die Spannung kurz nach dem
          Start, bevor die Lichtmaschine sie anhebt. Der Trend vergleicht die frühere mit der
          späteren Hälfte des Zeitraums; er ist ein Hinweis, keine Kapazitätsmessung.
        </p>
      </Tile>
    </>
  )
}

/** Von Hand gezeichnete Verlaufskurve aus dem Tagesminimum, mit einem Punkt je
    Messung. Der Tageshöchstwert bekommt bewusst KEINE eigene Linie mehr
    (Nutzerwunsch, Parität zum Panel) - er erscheint als Text, sobald ein Punkt
    angetippt wird, zusammen mit Datum und Uhrzeit.

    Die viewBox folgt der tatsächlich gemessenen Pixelbreite, statt eine feste
    320er-Fläche mit preserveAspectRatio="none" breitzuziehen: sonst würden die
    neuen Punkte auf breiten Anzeigen zu Ellipsen verzerrt (im Panel war
    dasselbe Problem an den auseinandergezogenen Achsenzahlen sichtbar). */
function Verlaufsdiagramm({ werte }: { werte: Tageswert[] }) {
  const hoehe = 120
  const rand = 8
  const svgRef = useRef<SVGSVGElement | null>(null)
  const [breite, setzeBreite] = useState(320)
  const [gewaehlt, setzeGewaehlt] = useState<number | null>(null)

  useEffect(() => {
    const el = svgRef.current
    if (!el || typeof ResizeObserver === "undefined") return
    const messen = () => {
      const w = el.getBoundingClientRect().width
      if (w > 0) setzeBreite(w)
    }
    messen()
    const beobachter = new ResizeObserver(messen)
    beobachter.observe(el)
    return () => beobachter.disconnect()
  }, [])

  // Feste Achse 10-15V (Nutzerwunsch): deckt den relevanten Bereich ab
  // (12V-Bleibatterie, Ladespannung des Alternators mit Reserve).
  const kleinster = 10
  const groesster = 15
  const spanne = groesster - kleinster

  const x = (i: number) => rand + (i / Math.max(1, werte.length - 1)) * (breite - 2 * rand)
  // Anteil auf [0,1] geklemmt statt roh extrapoliert - ein Ausreißer außerhalb
  // von kleinster/groesster zeichnet sich sonst weit außerhalb der Fläche.
  const y = (v: number) => {
    const anteil = Math.max(0, Math.min(1, (v - kleinster) / spanne))
    return hoehe - rand - anteil * (hoehe - 2 * rand)
  }

  const linie = werte
    .map((t, i) => (t.min == null ? null : `${i === 0 ? "M" : "L"}${x(i).toFixed(1)},${y(t.min).toFixed(1)}`))
    .filter(Boolean)
    .join(" ")

  const punktWaehlen = (ereignis: { clientX: number }) => {
    const el = svgRef.current
    if (!el || werte.length === 0) return
    const kasten = el.getBoundingClientRect()
    const relativ = ereignis.clientX - kasten.left - rand
    const schritt = (breite - 2 * rand) / Math.max(1, werte.length - 1)
    const index = Math.max(0, Math.min(werte.length - 1, Math.round(relativ / schritt)))
    setzeGewaehlt((vorher) => (vorher === index ? null : index))
  }

  const aktiv = gewaehlt != null ? werte[gewaehlt] : undefined
  const zeitpunkt = aktiv?.min_ts ? new Date(aktiv.min_ts) : null

  return (
    <>
      <svg
        ref={svgRef}
        className="dm-diagramm"
        viewBox={`0 0 ${breite.toFixed(1)} ${hoehe}`}
        role="img"
        aria-label={`Spannungsverlauf über ${werte.length} Messungen`}
        onClick={punktWaehlen}
      >
        <path d={linie} fill="none" stroke="var(--red)" strokeWidth="1.5" strokeLinejoin="round" />
        {werte.map((t, i) =>
          t.min == null ? null : (
            <circle
              key={t.datum}
              cx={x(i)}
              cy={y(t.min)}
              r={gewaehlt === i ? 4.5 : 2.4}
              fill="var(--red)"
              {...(gewaehlt === i ? { stroke: "var(--fg)", strokeWidth: 1 } : {})}
            />
          ),
        )}
      </svg>
      {aktiv && (
        <p className="dm-fussnote">
          {[
            zeitpunkt
              ? `${datum(aktiv.datum)} · ${zeitpunkt.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" })} Uhr`
              : datum(aktiv.datum),
            aktiv.min != null ? `Min ${de(aktiv.min, 2)} V` : null,
            aktiv.max != null ? `Max ${de(aktiv.max, 2)} V` : null,
            aktiv.min_temp_c != null ? `${de(aktiv.min_temp_c, 1)} °C` : null,
          ]
            .filter(Boolean)
            .join(" · ")}
        </p>
      )}
    </>
  )
}
