// Rein clientseitiges "Konto" (Phase 1, kein echtes Login/Backend) — genau wie der Warenkorb
// (siehe cart.ts) im localStorage gespeichert. Ermöglicht schon jetzt eine echte, durchgängige
// Demo: anmelden, Profil pflegen, Bestellungen bewerten, Kopfzeile reagiert überall darauf.
// TODO Phase 2: durch echtes Login (Cloudflare Worker + D1, siehe Tech-Stack-Entscheidung im
// Projekt-Vault) ersetzen — Struktur/Feldnamen bewusst schon so gewählt, dass sich eine echte
// Session später 1:1 einsetzen lässt, ohne Aufrufstellen in der ganzen Seite anpassen zu müssen.

import {
  type AboStufe,
  aboStufenKonfig,
  aboAktuellAktiv,
  teddyMeilensteine,
  naechsteMeilensteine,
  addMonateISO,
  heutigesDatumISO,
  aboEinstellungen,
} from "../data/abo";

export interface Account {
  loggedIn: boolean;
  /** Echter Name (fest, z.B. für Bestellungen/Lieferadresse) — Pflichtangabe, unabhängig vom
   *  optionalen Benutzernamen. */
  name: string;
  /** Optionaler Spitzname/Bonus-Anzeigename — wird bevorzugt angezeigt (z.B. Begrüßung, Header),
   *  wenn gesetzt, ersetzt aber NICHT den echten Namen (siehe `name`). */
  username: string;
  email: string;
  /** Profilfoto als data:-URL (im Browser aus der hochgeladenen Datei erzeugt). */
  photo: string;
  /** "<bestellnummer>:<produkt-slug>"-Paare, zu denen schon eine Rezension abgegeben wurde. */
  bewerteteBestellungen: string[];
  /** Treuebonus (siehe TREUE_ZIEL weiter unten): Anzahl gültiger, vollständig abgeschlossener
   *  und bezahlter Bestellungen seit der letzten Einlösung — zählt NICHT weiter, sobald der
   *  Bonus aktiv ist (siehe treuebonusAktiv), damit "10 von 10" nicht einfach weiterwächst,
   *  während die Belohnung noch auf ihre Einlösung wartet. */
  treueBestellungen: number;
  /** true, sobald treueBestellungen das Ziel erreicht hat UND die Belohnung noch nicht
   *  eingelöst wurde. Wird bei Einlösung wieder auf false gesetzt, gleichzeitig setzt
   *  treuebonusEinloesen() treueBestellungen zurück auf 0 — eindeutig diesem Konto zugeordnet,
   *  da alles im Konto-Objekt selbst liegt und nie kontoübergreifend gelesen wird. */
  treuebonusAktiv: boolean;
  /** Slug des Artikels, den die Person im Warenkorb für die -20%-Belohnung ausgewählt hat
   *  (siehe scripts/cart.ts / warenkorb-Seite). Wird bei Einlösung wieder geleert. */
  treuebonusArtikelSlug: string | null;
  /** Wie oft die Treuebelohnung insgesamt schon eingelöst wurde (über alle Zyklen hinweg,
   *  wird bei treuebonusEinloesen() hochgezählt, NIE zurückgesetzt) — für die "Bisher X× eingelöst"-
   *  Anzeige im Konto, macht den langfristigen Nutzen des Treuebonus sichtbar. */
  treuebonusEingeloestGesamt: number;
  /** ISO-Datum der letzten Einlösung, oder null, wenn noch nie eingelöst wurde. */
  treuebonusLetzteEinloesungDatum: string | null;
  /** Monatliches Abo (siehe data/abo.ts für Preise/Regeln + weiter unten für die Aktionen). */
  abo: AboZustand;
  /** Kurze In-Konto-Benachrichtigungen (Phase 1: kein E-Mail-/Push-Versand, siehe Hinweis bei
   *  benachrichtigen() weiter unten) — neueste zuerst. */
  benachrichtigungen: Benachrichtigung[];
}

export interface AboWechsel {
  stufe: AboStufe;
  von: string;
  bis: string;
  monate: number;
}

export interface AboZustand {
  stufe: AboStufe | null;
  startDatum: string | null;
  /** Ende des aktuell bezahlten Abrechnungszeitraums — solange "heute" davor liegt, gilt das
   *  Abo als aktiv (siehe aboAktuellAktiv() in data/abo.ts), unabhängig vom Kündigungsstatus. */
  bezahltBisDatum: string | null;
  /** Durchgehend erfolgreich bezahlte Monate in der AKTUELLEN, ununterbrochenen Abo-Stufe —
   *  Grundlage für die Teddy-Meilensteine (siehe teddyMeilensteine() in data/abo.ts). */
  bezahlteMonate: number;
  gekuendigt: boolean;
  gekuendigtAm: string | null;
  /** Lifetime-Zähler, wachsen nur beim tatsächlichen Überschreiten eines Meilensteins (siehe
   *  aboZahlungSimulieren()) — überleben Stufenwechsel/Kündigung/Wiederanmeldung unverändert,
   *  damit bereits verdiente Prämien nie verloren gehen. */
  kleinTeddyFreigeschaltetGesamt: number;
  grossTeddyFreigeschaltetGesamt: number;
  kleinTeddyEingeloest: number;
  grossTeddyEingeloest: number;
  /** WELCHE Teddys bereits eingelöst wurden — nicht nur wie viele. Wird beim Einlösen
   *  festgehalten und nie wieder verändert, damit die Kundin jederzeit nachsehen kann, welchen
   *  Teddy sie schon besitzt, und keinen doppelt auswählt. Bewusst als Verlauf gespeichert und
   *  NICHT aus der aktuellen Abo-Einstellung abgeleitet: VanVan kann die hinterlegten
   *  Teddy-Produkte jederzeit im Adminbereich austauschen — dann wäre eine nachträglich
   *  berechnete Liste falsch und würde einen Teddy anzeigen, den die Kundin nie bekommen hat. */
  erhalteneTeddys: ErhaltenerTeddy[];
  historie: AboWechsel[];
}

export interface ErhaltenerTeddy {
  art: "klein" | "gross";
  /** Produkt-Slug zum Zeitpunkt der Einlösung. */
  slug: string;
  /** Produktname beim Einlösen — dient nur als Rückfallebene, falls das Produkt später
   *  umbenannt oder aus dem Shop genommen wird. Die Anzeige bevorzugt den aktuellen,
   *  übersetzten Produktnamen. */
  name: string;
  /** Einlösedatum als ISO-Datum (JJJJ-MM-TT). */
  datum: string;
}

/** Feste Schlüssel statt fertigem Text — der eigentliche, sprachabhängige Satz wird erst beim
 *  Anzeigen im Kundenkonto übersetzt (siehe aboLabels.notifKeyLabels in konto/angemeldet.astro).
 *  So bleiben Benachrichtigungen auch auf EN/CH/FR-Seiten korrekt in der jeweiligen Sprache,
 *  obwohl dieses Skript selbst sprachneutral ist (kein Zugriff auf useTranslations()). */
export type BenachrichtigungKey =
  | "abo_abgeschlossen"
  | "abo_gewechselt"
  | "abo_zahlung_erfolgreich"
  | "abo_teddy_klein_frei"
  | "abo_teddy_gross_frei"
  | "abo_bald_praemie"
  | "abo_gekuendigt"
  | "abo_teddy_klein_eingeloest"
  | "abo_teddy_gross_eingeloest";

export interface Benachrichtigung {
  key: BenachrichtigungKey;
  /** Nur bei "abo_gewechselt" gesetzt — die neue Stufe, für den übersetzten Satz "{stufe}". */
  stufe?: AboStufe;
  datum: string;
  gelesen: boolean;
}

function leeresAbo(): AboZustand {
  return {
    stufe: null,
    startDatum: null,
    bezahltBisDatum: null,
    bezahlteMonate: 0,
    gekuendigt: false,
    gekuendigtAm: null,
    kleinTeddyFreigeschaltetGesamt: 0,
    grossTeddyFreigeschaltetGesamt: 0,
    kleinTeddyEingeloest: 0,
    grossTeddyEingeloest: 0,
    erhalteneTeddys: [],
    historie: [],
  };
}

const KEY = "vandiy_account_v1";

/** Ab dieser Anzahl gültiger, abgeschlossener Bestellungen wird die Treuebelohnung freigeschaltet. */
export const TREUE_ZIEL = 10;
/** Höhe des Treuebonus-Rabatts auf den frei gewählten Artikel. */
export const TREUE_RABATT_PROZENT = 20;

function leeresKonto(): Account {
  return {
    loggedIn: false,
    name: "",
    username: "",
    email: "",
    photo: "",
    bewerteteBestellungen: [],
    treueBestellungen: 0,
    treuebonusAktiv: false,
    treuebonusArtikelSlug: null,
    treuebonusEingeloestGesamt: 0,
    treuebonusLetzteEinloesungDatum: null,
    abo: leeresAbo(),
    benachrichtigungen: [],
  };
}

export function getAccount(): Account {
  if (typeof localStorage === "undefined") return leeresKonto();
  try {
    const basis = leeresKonto();
    const gespeichert = JSON.parse(localStorage.getItem(KEY) || "{}") ?? {};
    // Bewusst KEIN rein flaches Zusammenführen: Steht im Speicher ein fehlendes oder kaputtes
    // abo-Objekt (z.B. null aus einer älteren Fassung oder nach einem abgebrochenen Schreiben),
    // würde das den Standardwert überschreiben — und jeder Zugriff wie a.abo.bezahltBisDatum
    // wirft dann einen Fehler, der das GESAMTE Konto-Dashboard lahmlegt (keine Bestellungen,
    // keine Tabs mehr). Deshalb wird das verschachtelte abo-Objekt ausdrücklich mit den
    // Standardwerten aufgefüllt.
    const abo = { ...basis.abo, ...(gespeichert.abo ?? {}) };
    // Listen einzeln absichern: ein null/kaputter Wert aus dem Speicher darf hier nie
    // durchrutschen, sonst wirft der erste .map()/.length-Zugriff und legt die Seite lahm.
    if (!Array.isArray(abo.erhalteneTeddys)) abo.erhalteneTeddys = [];
    if (!Array.isArray(abo.historie)) abo.historie = [];
    return {
      ...basis,
      ...gespeichert,
      abo,
      bewerteteBestellungen: Array.isArray(gespeichert.bewerteteBestellungen) ? gespeichert.bewerteteBestellungen : basis.bewerteteBestellungen,
      benachrichtigungen: Array.isArray(gespeichert.benachrichtigungen) ? gespeichert.benachrichtigungen : basis.benachrichtigungen,
    };
  } catch {
    return leeresKonto();
  }
}

function saveAccount(a: Account) {
  localStorage.setItem(KEY, JSON.stringify(a));
  window.dispatchEvent(new CustomEvent("account:changed", { detail: a }));
}

export function isLoggedIn(): boolean {
  return getAccount().loggedIn;
}

/** Demo-Anmeldung: es gibt noch keine echte Passwortprüfung (Phase 2) — jede E-Mail meldet an,
 *  der ECHTE NAME wird beim ersten Mal aus dem E-Mail-Namen abgeleitet (Platzhalter, bis die
 *  Person ihn selbst einträgt), bleibt danach aber erhalten. Der Benutzername/Spitzname wird
 *  NICHT automatisch befüllt — er ist ein rein optionaler Bonus, den man sich selbst aussucht. */
export function login(email: string): Account {
  const bestehend = getAccount();
  const account: Account = {
    ...bestehend,
    loggedIn: true,
    email: email.trim(),
    name: bestehend.name || email.split("@")[0] || "Kundin",
  };
  saveAccount(account);
  return account;
}

export function logout() {
  saveAccount({ ...getAccount(), loggedIn: false });
}

/** Echter Name — feste Pflichtangabe (z.B. für Bestellungen/Lieferadresse), unabhängig vom
 *  optionalen Benutzernamen. */
export function setName(name: string) {
  saveAccount({ ...getAccount(), name: name.trim() });
}

/** Optionaler Spitzname/Bonus-Anzeigename — ersetzt NICHT den echten Namen. */
export function setUsername(username: string) {
  saveAccount({ ...getAccount(), username: username.trim() });
}

/** Anzeigename für Begrüßung/Header: bevorzugt den gewählten Spitznamen (persönlicher), fällt
 *  sonst auf den echten Namen zurück. */
export function anzeigeName(a: Account): string {
  return a.username || a.name;
}

export function setPhoto(dataUrl: string) {
  saveAccount({ ...getAccount(), photo: dataUrl });
}

export function bestellSchluessel(bestellnummer: string, produktSlug: string): string {
  return `${bestellnummer}:${produktSlug}`;
}

export function hatBestellungBewertet(bestellnummer: string, produktSlug: string): boolean {
  return getAccount().bewerteteBestellungen.includes(bestellSchluessel(bestellnummer, produktSlug));
}

export function markiereBestellungBewertet(bestellnummer: string, produktSlug: string) {
  const a = getAccount();
  const schluessel = bestellSchluessel(bestellnummer, produktSlug);
  if (!a.bewerteteBestellungen.includes(schluessel)) {
    saveAccount({ ...a, bewerteteBestellungen: [...a.bewerteteBestellungen, schluessel] });
  }
}

// ─── Treuebonus (10 abgeschlossene Bestellungen → 20% auf einen frei wählbaren Artikel) ───
//
// WICHTIG: Es zählt hier bewusst nur eine echte "Bestellung abgeschlossen UND bezahlt"-Aktion
// (siehe registriereAbgeschlosseneBestellung(), wird im Checkout nach erfolgreichem Absenden
// aufgerufen — nicht z.B. schon beim Warenkorb füllen). Stornierte/nicht bezahlte Bestellungen
// rufen diese Funktion nie auf und zählen dadurch automatisch nicht mit. In Phase 1 (kein
// echtes Zahlungsbackend) gilt jede erfolgreich abgeschickte Demo-Bestellung als "bezahlt";
// TODO Phase 2: hier stattdessen auf ein echtes Zahlungs-Webhook-Ereignis reagieren.

/** Zählt eine gültige, abgeschlossene Bestellung für den Treuebonus. Zählt NICHT weiter, wenn
 *  gerade schon eine Belohnung auf Einlösung wartet (erst einlösen, dann läuft der nächste
 *  Zähler wieder bei 0 los). Schaltet die Belohnung automatisch frei, sobald TREUE_ZIEL erreicht
 *  ist.
 *
 *  Der Treuebonus ist bewusst an ein AKTIVES Abo gekoppelt (siehe Kundenwunsch: soll als
 *  zusätzlicher Anreiz fürs Abo wirken, nicht unabhängig davon nutzbar sein) — ohne aktives,
 *  bezahltes Abo bleibt der Fortschritt einfach stehen, statt weiterzuzählen. Sobald wieder ein
 *  Abo aktiv ist, geht es von genau diesem Stand aus weiter (kein Reset auf 0). */
export function registriereAbgeschlosseneBestellung(): Account {
  const a = getAccount();
  if (a.treuebonusAktiv) return a;
  if (!aboStatus(a).aktiv) return a;
  const naechsterStand = Math.min(TREUE_ZIEL, a.treueBestellungen + 1);
  const updated: Account = {
    ...a,
    treueBestellungen: naechsterStand,
    treuebonusAktiv: naechsterStand >= TREUE_ZIEL,
  };
  saveAccount(updated);
  return updated;
}

/** Legt fest, auf welchen Artikel im Warenkorb der -20%-Treuebonus angewendet werden soll
 *  (oder hebt die Auswahl mit `null` wieder auf) — frei wählbar, siehe Pflichtenheft. */
export function setTreuebonusArtikel(slug: string | null) {
  saveAccount({ ...getAccount(), treuebonusArtikelSlug: slug });
}

/** Markiert die Belohnung als eingelöst: deaktiviert sie, setzt den Zähler für den nächsten
 *  Durchlauf auf 0 zurück und leert die Artikelauswahl. Wird im Checkout nach erfolgreichem
 *  Absenden einer Bestellung aufgerufen, die den Treuebonus tatsächlich genutzt hat. */
export function treuebonusEinloesen(): Account {
  const updated: Account = {
    ...getAccount(),
    treueBestellungen: 0,
    treuebonusAktiv: false,
    treuebonusArtikelSlug: null,
    treuebonusEingeloestGesamt: getAccount().treuebonusEingeloestGesamt + 1,
    treuebonusLetzteEinloesungDatum: heutigesDatumISO(),
  };
  saveAccount(updated);
  return updated;
}

export interface TreueFortschritt {
  aktuelle: number;
  ziel: number;
  fehlende: number;
  aktiv: boolean;
  /** true, solange KEIN aktives Abo besteht — der Treuebonus ist dann als Anreiz sichtbar, aber
   *  gesperrt (sammelt nicht weiter, kann nicht eingelöst werden), bis ein Abo abgeschlossen wird. */
  gesperrt: boolean;
  /** Rabatt-Prozentsatz der Belohnung (aktuell fest 20%, siehe TREUE_RABATT_PROZENT) — als Teil
   *  des Fortschritts mitgegeben, damit die Anzeige nicht selbst importieren/duplizieren muss. */
  rabattProzent: number;
  /** Wie oft die Belohnung insgesamt schon eingelöst wurde + wann zuletzt (siehe Account-Felder
   *  oben) — für die erweiterte Fortschrittskarte im Konto. */
  eingeloestGesamt: number;
  letzteEinloesungDatum: string | null;
}

/** Aufbereiteter Fortschritt fürs Anzeigen im Kundenkonto ("7 von 10", "Dir fehlen noch 3 …"). */
export function treueFortschritt(a: Account): TreueFortschritt {
  return {
    aktuelle: a.treueBestellungen,
    ziel: TREUE_ZIEL,
    fehlende: Math.max(0, TREUE_ZIEL - a.treueBestellungen),
    aktiv: a.treuebonusAktiv,
    gesperrt: !aboStatus(a).aktiv,
    rabattProzent: TREUE_RABATT_PROZENT,
    eingeloestGesamt: a.treuebonusEingeloestGesamt,
    letzteEinloesungDatum: a.treuebonusLetzteEinloesungDatum,
  };
}

// ─── Abo-System (monatliche Mitgliedschaft mit dauerhaftem Bestellrabatt + Teddy-Prämien) ───
//
// Phase 1 (kein echtes Zahlungsbackend): aboZahlungSimulieren() steht für den monatlichen
// Abbuchungs-Webhook, den ein echter Zahlungsanbieter in Phase 2 auslösen würde — genau wie
// registriereAbgeschlosseneBestellung() oben schon für echte Bestell-Zahlungen steht. Nur
// Klicks auf diese Funktion zählen als "erfolgreich bezahlter Monat"; nichts hier läuft von
// selbst im Hintergrund, es gibt (noch) keinen Cronjob/Webhook, der das für echtes Geld täte.
//
// "Benachrichtigungen" sind hier bewusst als einfache In-Konto-Liste umgesetzt (siehe
// benachrichtigen()), NICHT als echte E-Mail/Push — das bräuchte einen Mailversand-Dienst, den
// es in Phase 1 nicht gibt. Deckt aber inhaltlich dieselben Ereignisse ab.

function benachrichtigen(a: Account, key: BenachrichtigungKey, stufe?: AboStufe): Account {
  const eintrag: Benachrichtigung = { key, stufe, datum: heutigesDatumISO(), gelesen: false };
  return { ...a, benachrichtigungen: [eintrag, ...a.benachrichtigungen].slice(0, 30) };
}

export function alleBenachrichtigungenGelesen() {
  const a = getAccount();
  saveAccount({ ...a, benachrichtigungen: a.benachrichtigungen.map((b) => ({ ...b, gelesen: true })) });
}

/** Abo neu abschließen ODER in eine andere Stufe wechseln. Bei einem Wechsel wird die bisherige
 *  Stufe mit ihren gesammelten Monaten in die Historie geschrieben (siehe Adminbereich-Regel
 *  `monateUeberStufenZusammenzaehlen`, ob die Monate in die neue Stufe übernommen werden). Bei
 *  einer Wiederanmeldung nach vollständig ausgelaufenem Abo greift stattdessen die Regel
 *  `fortsetzungBeiWiederanmeldung`. Verhindert doppelte Abbuchungen, indem eine laufende Stufe
 *  IMMER erst sauber beendet wird, bevor die neue beginnt. */
export function aboAbschliessen(stufe: AboStufe): Account {
  let a = getAccount();
  const heute = heutigesDatumISO();
  const bisherStufe = a.abo.stufe;
  const warNochAktivOderWartend = bisherStufe !== null;
  const istWechsel = warNochAktivOderWartend && bisherStufe !== stufe;
  const wiederanmeldungNachLapse = !warNochAktivOderWartend && a.abo.bezahlteMonate === 0 && a.abo.historie.length > 0;

  let neueMonate = 0;
  let historie = a.abo.historie;
  if (warNochAktivOderWartend) {
    // Laufende (oder gekündigt-aber-noch-bezahlte) Stufe sauber abschließen, BEVOR die neue
    // beginnt — verhindert, dass beide gleichzeitig als "aktiv" gelten und doppelt abgebucht
    // würde.
    historie = [...historie, { stufe: bisherStufe!, von: a.abo.startDatum!, bis: heute, monate: a.abo.bezahlteMonate }];
    if (istWechsel && aboEinstellungen.monateUeberStufenZusammenzaehlen) neueMonate = a.abo.bezahlteMonate;
  } else if (wiederanmeldungNachLapse && aboEinstellungen.fortsetzungBeiWiederanmeldung) {
    neueMonate = a.abo.historie[a.abo.historie.length - 1]?.monate ?? 0;
  }

  a = {
    ...a,
    abo: {
      ...a.abo,
      stufe,
      startDatum: heute,
      bezahltBisDatum: addMonateISO(heute, 1),
      bezahlteMonate: neueMonate,
      gekuendigt: false,
      gekuendigtAm: null,
      historie,
    },
  };
  a = benachrichtigen(a, istWechsel ? "abo_gewechselt" : "abo_abgeschlossen", istWechsel ? stufe : undefined);
  saveAccount(a);
  return a;
}

/** Steht für die monatliche Abo-Abbuchung (siehe Hinweis oben). Verlängert den bezahlten
 *  Zeitraum um einen weiteren Monat, erhöht den Monatszähler und schaltet dabei automatisch
 *  neue Teddy-Meilensteine frei, sobald sie überschritten werden — jeder Meilenstein wird
 *  garantiert nur EIN einziges Mal gezählt (Differenz aus Vorher-/Nachher-Berechnung). Gekündigte
 *  Abos zahlen nicht weiter nach (ihre Vorteile laufen einfach zum bezahltBisDatum aus). */
export function aboZahlungSimulieren(): Account {
  let a = getAccount();
  if (!a.abo.stufe || a.abo.gekuendigt) return a;

  const vorher = teddyMeilensteine(a.abo.stufe, a.abo.bezahlteMonate);
  const neueMonate = a.abo.bezahlteMonate + 1;
  const nachher = teddyMeilensteine(a.abo.stufe, neueMonate);
  const basisDatum = a.abo.bezahltBisDatum && aboAktuellAktiv(a.abo.bezahltBisDatum) ? a.abo.bezahltBisDatum : heutigesDatumISO();

  a = {
    ...a,
    abo: {
      ...a.abo,
      bezahlteMonate: neueMonate,
      bezahltBisDatum: addMonateISO(basisDatum, 1),
      kleinTeddyFreigeschaltetGesamt: a.abo.kleinTeddyFreigeschaltetGesamt + (nachher.klein - vorher.klein),
      grossTeddyFreigeschaltetGesamt: a.abo.grossTeddyFreigeschaltetGesamt + (nachher.gross - vorher.gross),
    },
  };
  a = benachrichtigen(a, "abo_zahlung_erfolgreich");
  if (nachher.klein > vorher.klein) a = benachrichtigen(a, "abo_teddy_klein_frei");
  if (nachher.gross > vorher.gross) a = benachrichtigen(a, "abo_teddy_gross_frei");
  // Kurz VOR dem nächsten Meilenstein zusätzlich vorwarnen (ein Monat vorher).
  const rest = naechsteMeilensteine(a.abo.stufe, neueMonate);
  if (rest.klein === 1 || rest.gross === 1) a = benachrichtigen(a, "abo_bald_praemie");
  saveAccount(a);
  return a;
}

/** Kündigt zum Ende der bereits bezahlten Laufzeit — Vorteile (Rabatt, weitere
 *  Monatszählung/Meilensteine) laufen dann automatisch über aboAktuellAktiv()/aboZahlungSimulieren()
 *  aus, ohne dass hier sofort etwas "hart" abgeschaltet wird. */
export function aboKuendigen(): Account {
  let a = getAccount();
  if (!a.abo.stufe || a.abo.gekuendigt) return a;
  a = { ...a, abo: { ...a.abo, gekuendigt: true, gekuendigtAm: heutigesDatumISO() } };
  a = benachrichtigen(a, "abo_gekuendigt");
  saveAccount(a);
  return a;
}

/** Löst eine freigeschaltete, noch nicht eingelöste Teddy-Prämie ein — schreibt sie fest auf
 *  dieses Konto (Lifetime-Zähler steigt), damit sie nicht doppelt eingelöst werden kann. Das
 *  eigentliche Hinzufügen zum Warenkorb übernimmt die aufrufende Seite (siehe konto/angemeldet). */
/** Löst eine verdiente Teddy-Prämie ein. `produkt` sagt, WELCHER Teddy tatsächlich vergeben
 *  wurde (Slug + Name) — das wird dauerhaft im Konto festgehalten, damit die Kundin später
 *  sieht, welchen Teddy sie schon hat, und keinen doppelt nimmt. Ohne Angabe wird auf den
 *  aktuell hinterlegten Teddy der Abo-Einstellungen zurückgegriffen. */
export function teddyEinloesen(art: "klein" | "gross", produkt?: { slug: string; name: string }): Account {
  let a = getAccount();
  const verfuegbar = art === "klein"
    ? a.abo.kleinTeddyFreigeschaltetGesamt - a.abo.kleinTeddyEingeloest
    : a.abo.grossTeddyFreigeschaltetGesamt - a.abo.grossTeddyEingeloest;
  if (verfuegbar <= 0) return a;
  const eintrag: ErhaltenerTeddy = {
    art,
    slug: produkt?.slug ?? (art === "klein" ? aboEinstellungen.kleinTeddySlug : aboEinstellungen.grossTeddySlug),
    name: produkt?.name ?? "",
    datum: heutigesDatumISO(),
  };
  a = { ...a, abo: { ...a.abo, erhalteneTeddys: [...a.abo.erhalteneTeddys, eintrag] } };
  a = art === "klein"
    ? { ...a, abo: { ...a.abo, kleinTeddyEingeloest: a.abo.kleinTeddyEingeloest + 1 } }
    : { ...a, abo: { ...a.abo, grossTeddyEingeloest: a.abo.grossTeddyEingeloest + 1 } };
  a = benachrichtigen(a, art === "klein" ? "abo_teddy_klein_eingeloest" : "abo_teddy_gross_eingeloest");
  saveAccount(a);
  return a;
}

export interface AboStatus {
  stufe: AboStufe | null;
  aktiv: boolean;
  rabattProzent: number;
  preis: number;
  bezahlteMonate: number;
  naechsterKleinIn: number | null;
  naechsterGrossIn: number | null;
  kleinVerfuegbar: number;
  grossVerfuegbar: number;
}

/** Aufbereiteter Gesamtstatus fürs Kundenkonto — bündelt Aktiv-Check, aktuellen Rabattsatz und
 *  Prämien-Fortschritt an einer Stelle, damit UI-Code nicht selbst rechnen muss. */
export function aboStatus(a: Account): AboStatus {
  const aktiv = aboAktuellAktiv(a.abo.bezahltBisDatum);
  const stufe = a.abo.stufe;
  const konfig = stufe ? aboStufenKonfig(stufe) : null;
  const rest = stufe ? naechsteMeilensteine(stufe, a.abo.bezahlteMonate) : { klein: null, gross: null };
  return {
    stufe,
    aktiv,
    rabattProzent: aktiv && konfig ? konfig.rabattProzent : 0,
    preis: konfig?.preis ?? 0,
    bezahlteMonate: a.abo.bezahlteMonate,
    naechsterKleinIn: rest.klein,
    naechsterGrossIn: rest.gross,
    kleinVerfuegbar: Math.max(0, a.abo.kleinTeddyFreigeschaltetGesamt - a.abo.kleinTeddyEingeloest),
    grossVerfuegbar: Math.max(0, a.abo.grossTeddyFreigeschaltetGesamt - a.abo.grossTeddyEingeloest),
  };
}
