// Rein clientseitiger Warenkorb (Phase 1, kein Backend). Speichert im localStorage.
// TODO Phase 2: durch echten Cloudflare-Worker-Warenkorb/Checkout ersetzen.

import { getProduct } from "../data/products";
import { effektiverPreis, effektiverZeilenpreis, istImSale } from "../data/rabatt";
import { findeGutschein, gutscheinRabatt, type Gutschein } from "../data/gutscheine";
import { findePartnercode, partnercodeRabatt, type Partnercode } from "../data/partnercodes";
import { getAccount, TREUE_RABATT_PROZENT, aboStatus } from "./account";
import { aboEinstellungen } from "../data/abo";

export interface CartItem {
  slug: string;
  name: string;
  preis: number;
  menge: number;
  // Ob dieser Artikel gerade gekauft werden soll oder nur "im Warenkorb liegen bleibt" für
  // später. Fehlt das Feld (ältere, bereits gespeicherte Warenkörbe), gilt es als ausgewählt —
  // siehe istAusgewaehlt().
  ausgewaehlt?: boolean;
  /** true bei einer eingelösten Teddy-Prämie aus dem Abo-System (siehe fuegeTeddyGratisHinzu())
   *  — kostet 0 €, unabhängig vom eigentlichen Produktpreis (der bleibt für die Anzeige "regulär
   *  X €, für dich gratis" erhalten). */
  gratis?: boolean;
}

/** true, sofern nicht explizit auf false gesetzt — hält ältere localStorage-Warenkörbe ohne
 *  dieses Feld weiterhin vollständig "ausgewählt" (kein Verhaltensbruch für Bestandsbesucher). */
export function istAusgewaehlt(item: CartItem): boolean {
  return item.ausgewaehlt !== false;
}

/** Zeilenpreis EINER Warenkorb-Position — 0 €, wenn es sich um eine eingelöste Gratis-Prämie
 *  handelt, sonst der normale (rabattierte) Zeilenpreis. Zentrale Stelle, damit Warenkorb-Liste,
 *  Zwischensumme UND Checkout-Beleg garantiert denselben Betrag zeigen. */
export function zeilenpreisFuer(item: CartItem): number {
  if (item.gratis) return 0;
  const produkt = getProduct(item.slug);
  return produkt ? effektiverZeilenpreis(produkt, item.menge) : item.menge * item.preis;
}

const KEY = "vandiy_cart_v1";

export function getCart(): CartItem[] {
  if (typeof localStorage === "undefined") return [];
  try {
    return JSON.parse(localStorage.getItem(KEY) || "[]");
  } catch {
    return [];
  }
}

export function saveCart(items: CartItem[]) {
  localStorage.setItem(KEY, JSON.stringify(items));
  window.dispatchEvent(new CustomEvent("cart:changed", { detail: items }));
}

/** Nie mehr als tatsächlich auf Lager ist — greift unabhängig davon, welche Oberfläche
 *  addToCart()/updateQuantity() aufruft, damit diese Regel garantiert eingehalten wird und
 *  nicht nur an einer einzelnen Stelle in der UI. */
function maxBestand(slug: string): number {
  const produkt = getProduct(slug);
  return produkt ? produkt.bestand : Infinity;
}

export function addToCart(item: Omit<CartItem, "menge">, menge = 1) {
  const cart = getCart();
  const max = maxBestand(item.slug);
  const existing = cart.find((i) => i.slug === item.slug);
  if (existing) {
    existing.menge = Math.min(existing.menge + menge, max);
  } else {
    cart.push({ ...item, menge: Math.min(Math.max(1, menge), max) });
  }
  saveCart(cart);
}

export function updateQuantity(slug: string, menge: number) {
  let cart = getCart();
  menge = Math.min(menge, maxBestand(slug));
  if (menge <= 0) {
    cart = cart.filter((i) => i.slug !== slug);
  } else {
    const item = cart.find((i) => i.slug === slug);
    if (item) item.menge = menge;
  }
  saveCart(cart);
}

export function removeFromCart(slug: string) {
  saveCart(getCart().filter((i) => i.slug !== slug));
}

/** Legt fest, ob ein Artikel jetzt gekauft werden soll (true) oder nur im Warenkorb liegen
 *  bleibt, ohne mitgekauft zu werden (false) — z.B. wenn man sich noch nicht sicher ist. */
export function setAusgewaehlt(slug: string, ausgewaehlt: boolean) {
  const cart = getCart();
  const item = cart.find((i) => i.slug === slug);
  if (item) item.ausgewaehlt = ausgewaehlt;
  saveCart(cart);
}

export function cartCount(): number {
  return getCart().reduce((sum, i) => sum + i.menge, 0);
}

// Zwischensumme inkl. Produkt-Rabatt (Prozent/Betrag) UND Mengenrabatt-Staffel — schaut sich
// dafür live die aktuellen Produktdaten an (nicht den bei addToCart gespeicherten Preis), damit
// ein nachträglich geänderter/abgelaufener Rabatt sich sofort korrekt auswirkt. Zählt NUR
// Artikel, die gerade zum Kauf ausgewählt sind — abgewählte Artikel bleiben im Warenkorb liegen,
// fließen aber nicht in Summe/Checkout ein.
export function cartTotal(): number {
  return getCart()
    .filter(istAusgewaehlt)
    .reduce((sum, i) => sum + zeilenpreisFuer(i), 0);
}

/** Fügt eine eingelöste Teddy-Prämie (siehe scripts/account.ts → teddyEinloesen()) als echten,
 *  aber kostenlosen Artikel zum Warenkorb hinzu — taucht ganz normal in der Liste auf, zählt
 *  aber mit 0 € in die Summe (siehe zeilenpreisFuer()).
 *  Gibt zurück, WELCHER Teddy es war — der Aufrufer hält das im Konto fest, damit die Kundin
 *  später sieht, welchen Teddy sie schon besitzt (siehe ErhaltenerTeddy in account.ts). */
export function fuegeTeddyGratisHinzu(art: "klein" | "gross"): { slug: string; name: string } {
  const slug = art === "klein" ? aboEinstellungen.kleinTeddySlug : aboEinstellungen.grossTeddySlug;
  const produkt = getProduct(slug);
  const name = produkt?.name.de ?? (art === "klein" ? "Kleiner Teddy" : "Großer Teddy");
  const cart = getCart();
  const existing = cart.find((i) => i.slug === slug && i.gratis);
  if (existing) {
    existing.menge += 1;
  } else {
    cart.push({ slug, name, preis: produkt?.preis ?? 0, menge: 1, ausgewaehlt: true, gratis: true });
  }
  saveCart(cart);
  return { slug, name };
}

const COUPON_KEY = "vandiy_coupon_v1";

export function getCouponCode(): string {
  if (typeof localStorage === "undefined") return "";
  return localStorage.getItem(COUPON_KEY) || "";
}

export function setCouponCode(code: string) {
  localStorage.setItem(COUPON_KEY, code.trim());
  window.dispatchEvent(new CustomEvent("cart:changed", { detail: getCart() }));
}

export function clearCoupon() {
  localStorage.removeItem(COUPON_KEY);
  window.dispatchEvent(new CustomEvent("cart:changed", { detail: getCart() }));
}

/** Der aktuell eingelöste Gutschein, falls der gespeicherte Code (noch) gültig ist. */
export function appliedCoupon(): Gutschein | undefined {
  const code = getCouponCode();
  if (!code) return undefined;
  return findeGutschein(code);
}

/** Rabatt-Betrag in Euro, den der eingelöste Gutschein auf die gegebene Zwischensumme gibt. */
export function couponDiscount(subtotal: number): number {
  const g = appliedCoupon();
  return g ? gutscheinRabatt(g, subtotal) : 0;
}

// Partner-/Influencer-Rabattcodes — eigener, unabhängiger "Slot" neben dem allgemeinen
// Gutscheincode (siehe src/data/partnercodes.ts für den fachlichen Hintergrund). Eigener
// localStorage-Schlüssel, damit beide Codes unabhängig voneinander gesetzt/entfernt werden können.
const PARTNER_CODE_KEY = "vandiy_partnercode_v1";

export function getPartnerCode(): string {
  if (typeof localStorage === "undefined") return "";
  return localStorage.getItem(PARTNER_CODE_KEY) || "";
}

export function setPartnerCode(code: string) {
  localStorage.setItem(PARTNER_CODE_KEY, code.trim());
  window.dispatchEvent(new CustomEvent("cart:changed", { detail: getCart() }));
}

export function clearPartnerCode() {
  localStorage.removeItem(PARTNER_CODE_KEY);
  window.dispatchEvent(new CustomEvent("cart:changed", { detail: getCart() }));
}

/** Der aktuell eingelöste Partnercode, falls der gespeicherte Code (noch) gültig ist. */
export function appliedPartnerCode(): Partnercode | undefined {
  const code = getPartnerCode();
  if (!code) return undefined;
  return findePartnercode(code);
}

/** Warenwert, auf den der aktuell eingelöste Partnercode überhaupt angewendet werden darf. Ist
 *  `giltAufSale` am Code auf false gesetzt, zählen NUR Zeilen mit regulärem Preis (kein
 *  preisAlt, kein aktiver Prozent-/Betrags-Rabatt) — schon reduzierte Artikel bleiben dann außen
 *  vor, damit sich der Partner-Rabatt nicht zusätzlich auf einen bereits reduzierten Preis
 *  draufsummiert. Ohne Angabe (Standard) zählt wie bisher der komplette Warenwert. */
export function partnerCodeEligibleSubtotal(): number {
  const p = appliedPartnerCode();
  if (!p) return 0;
  return getCart()
    .filter(istAusgewaehlt)
    .reduce((sum, i) => {
      const produkt = getProduct(i.slug);
      if (!produkt) return sum + i.menge * i.preis;
      if (p.giltAufSale === false && istImSale(produkt)) return sum;
      return sum + effektiverZeilenpreis(produkt, i.menge);
    }, 0);
}

/** Rabatt-Betrag in Euro, den der eingelöste Partnercode gibt — berechnet auf Basis von
 *  partnerCodeEligibleSubtotal(), NICHT auf der kompletten übergebenen Zwischensumme (siehe dort
 *  für den Hintergrund zu `giltAufSale`). Der Parameter bleibt der Vollständigkeit halber
 *  bestehen (identische Signatur wie couponDiscount), wird aber nicht mehr direkt verwendet. */
export function partnerCodeDiscount(_subtotal?: number): number {
  const p = appliedPartnerCode();
  return p ? partnercodeRabatt(p, partnerCodeEligibleSubtotal()) : 0;
}

// Treuebonus: 20% auf GENAU EINEN frei gewählten Artikel (siehe scripts/account.ts für die
// Fortschritts-/Freischalt-Logik). Die Auswahl (treuebonusArtikelSlug) liegt im Konto, nicht im
// Warenkorb selbst — bleibt so auch über einen geleerten/neu befüllten Warenkorb hinweg
// eindeutig diesem Kundenkonto zugeordnet.

/** Rabatt in Euro, den der aktive Treuebonus auf den gewählten Artikel gibt — 0, wenn kein
 *  Bonus aktiv ist, kein Artikel gewählt wurde, oder der gewählte Artikel gerade nicht (mehr)
 *  zum Kauf ausgewählt im Warenkorb liegt. Gilt bewusst nur für EIN Stück der Position, nicht
 *  mehrfach bei größerer Menge — "ein frei wählbarer Artikel", kein Mengenrabatt-Ersatz.
 *
 *  Der Treuebonus ist an ein AKTIVES Abo gekoppelt (siehe account.ts) — falls das Abo zwischen
 *  Freischaltung und Einlösung ausläuft, bleibt die Belohnung zwar erhalten, greift aber erst
 *  wieder, sobald erneut ein Abo aktiv ist. */
export function treuebonusDiscount(): number {
  const a = getAccount();
  if (!a.treuebonusAktiv || !a.treuebonusArtikelSlug) return 0;
  if (!aboStatus(a).aktiv) return 0;
  const item = getCart().find((i) => i.slug === a.treuebonusArtikelSlug);
  if (!item || !istAusgewaehlt(item)) return 0;
  const produkt = getProduct(item.slug);
  const einzelpreis = produkt ? effektiverPreis(produkt) : item.preis;
  return einzelpreis * (TREUE_RABATT_PROZENT / 100);
}

// Abo-Rabatt (siehe scripts/account.ts + data/abo.ts): gilt automatisch auf JEDE Bestellung,
// solange ein Abo aktiv und bezahlt ist — kein Code nötig. Ob er sich mit Gutschein/Partnercode/
// Treuebonus kombinieren lässt, legt VanVan im Adminbereich fest (kombinierbarMitAnderenRabatten).

/** Abo-Rabatt in Euro auf die komplette Zwischensumme (0, wenn kein Abo aktiv ist). */
export function aboDiscount(subtotal: number): number {
  const { aktiv, rabattProzent } = aboStatus(getAccount());
  return aktiv && rabattProzent > 0 ? subtotal * (rabattProzent / 100) : 0;
}

/** Gutschein, Partnercode, Treuebonus und Abo-Rabatt können grundsätzlich gleichzeitig aktiv
 *  sein. Ist der Abo-Rabatt NICHT mit anderen Rabatten kombinierbar (Adminbereich-Einstellung),
 *  gilt bei aktivem Abo ausschließlich dieser — alle anderen werden für diese Bestellung
 *  ignoriert. Die Summe wird immer auf die Zwischensumme gedeckelt, damit der Gesamtpreis nie
 *  unter 0 € rutscht. */
export function totalDiscount(subtotal: number): number {
  const abo = aboDiscount(subtotal);
  if (abo > 0 && !aboEinstellungen.kombinierbarMitAnderenRabatten) {
    return Math.min(subtotal, abo);
  }
  return Math.min(subtotal, couponDiscount(subtotal) + partnerCodeDiscount(subtotal) + treuebonusDiscount() + abo);
}
