"use client";

import { useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";
import Link from "next/link";
import Image from "next/image";
import { useCart, removeFromCart, setQuantity } from "../../lib/cart";
import { useProducts } from "../../lib/products";
import { useDiscount, applyDiscount, clearDiscount } from "../../lib/discount";
import { computeSubtotal, computeCartTotals, effectivePrice, effectiveTaxRate, cartHasShippableItem } from "../../lib/cartTotals";
import { computeTaxBreakdown } from "@einfach-produktiv/invoicing";
import { formatPrice, discountPercent } from "../../lib/format";
import { Reveal } from "../../components/Reveal";
import { VersandModal } from "../../components/VersandModal";
import { VatBreakdown } from "../../components/VatBreakdown";
import { ArrowLeftIcon } from "../../components/ArrowLeftIcon";
import { ProductName } from "../../components/ProductName";
import { FreeShippingBanner } from "./FreeShippingBanner";
import type { TrustBadge, ShippingSettings } from "../../lib/payload";

export function CartContent({
  trustBadges,
  shippingCost,
  freeShippingThreshold,
  shippingSettings,
  defaultTaxRate,
  kleinunternehmer,
  showDiscountField,
}: {
  trustBadges: TrustBadge[];
  /** Price of the default (first active, i.e. Standard) ShippingMethod — an
   * estimate, since the cart doesn't ask which method the shopper wants
   * yet (that's /checkout). */
  shippingCost: number;
  /** Lowest freeShippingThreshold among active ShippingMethods, or null if
   * none has one (in which case FreeShippingBanner just doesn't render). */
  freeShippingThreshold: number | null;
  /** Delivery-time disclosure (Payload's Shipping Settings), fetched by the
   * page and threaded down here — this is a Client Component, so it can't
   * fetch it itself. Also passed straight through to VersandModal. Named
   * "shippingSettings", not "shipping" — that name is already the local
   * computed shipping-cost value below. */
  shippingSettings: ShippingSettings;
  /** Tenant's default VAT rate (Company Settings), for products that don't
   * override taxRatePercent themselves — see lib/cartTotals.ts's
   * effectiveTaxRate(). */
  defaultTaxRate: number;
  /** §19 UStG — this tenant's company-settings.kleinunternehmer (Payload's
   * lib/payload.ts's getKleinunternehmer(), same ISR freshness as
   * defaultTaxRate above). Drops the "inkl. X% MwSt." hints and the VAT
   * breakdown in favor of the §19 notice below. */
  kleinunternehmer: boolean;
  /** Whether Payload currently has at least one active discount code at
   * all (lib/discountServer.ts's hasActiveDiscountCode()) — no point
   * showing an open "enter a code" field when nothing could ever validate
   * against it. Only gates the manual-entry form; a code already applied
   * (e.g. from an earlier session, or one deactivated after being shared)
   * still shows its own result row regardless. */
  showDiscountField: boolean;
}) {
  const [versandOpen, setVersandOpen] = useState(false);
  const cart = useCart();
  const products = useProducts();
  const discount = useDiscount();
  const [discountInput, setDiscountInput] = useState("");
  const [discountError, setDiscountError] = useState<string | null>(null);
  const [discountLoading, setDiscountLoading] = useState(false);
  const searchParams = useSearchParams();
  // While the /api/products fetch is still pending, treat a non-empty
  // cart as "loading" rather than "empty" — the old hardcoded PRODUCTS
  // lookup was synchronous, so this distinction didn't exist before;
  // without it, a returning shopper with items already in their cart
  // would briefly see the empty-cart message flash before their real
  // cart content gets a chance to render.
  const productsLoading = products.length === 0 && cart.length > 0;
  const items = cart
    .map((entry) => ({ entry, product: products.find((p) => p.id === entry.id) }))
    .filter((row): row is { entry: typeof cart[number]; product: NonNullable<(typeof row)["product"]> } => Boolean(row.product));

  const subtotal = computeSubtotal(items);
  const shipping =
    items.length === 0 || !cartHasShippableItem(items) || (freeShippingThreshold !== null && subtotal >= freeShippingThreshold)
      ? 0
      : shippingCost;
  const { totalSavings, discountAmount, total } = computeCartTotals(items, shipping, discount);
  const taxBreakdown = computeTaxBreakdown(
    items.map(({ entry, product }) => ({
      quantity: entry.qty,
      unitPrice: effectivePrice(entry, product),
      taxRatePercent: effectiveTaxRate(product, defaultTaxRate),
    })),
    subtotal,
    discountAmount,
    shipping,
  );

  async function handleApplyDiscount(code: string) {
    if (!code) return;
    setDiscountLoading(true);
    setDiscountError(null);
    try {
      const res = await fetch("/api/discount/validate", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ code, subtotal }),
      });
      const data = await res.json();
      if (data.valid) {
        applyDiscount({ code: code.toUpperCase(), type: data.type, value: data.value });
        setDiscountInput("");
      } else {
        setDiscountError(data.reason || "Dieser Code ist ungültig.");
      }
    } catch {
      setDiscountError("Rabattcode konnte gerade nicht geprüft werden.");
    } finally {
      setDiscountLoading(false);
    }
  }

  // No manual input field anymore (see the Rabattcode section below) —
  // codes are shared as a direct link instead (e.g. "/cart?code=SAVE10"),
  // auto-applied once on arrival. Only fires while nothing's applied yet
  // and there's actually something in the cart to validate a minimum-order
  // value against.
  useEffect(() => {
    const code = searchParams.get("code");
    if (code && !discount && items.length > 0) {
      // eslint-disable-next-line react-hooks/set-state-in-effect -- one-time sync from the URL's ?code= param to app state on arrival, via an async server validation call, not a render-cascade
      handleApplyDiscount(code);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps -- only ever re-run when the cart finishes loading or the URL's code param itself changes, not on every discount/handleApplyDiscount identity change
  }, [searchParams, items.length]);

  return (
    <>
      {/* Page header */}
      <Reveal className="flex flex-col gap-3 items-start pb-6 pt-10 px-[var(--layout-padding-x)] w-full">
        <p className="flex items-center gap-2 text-body-sm text-text-muted">
          <Link href="/" className="hover:text-brand transition-colors">Startseite</Link>
          <span>›</span>
          <span className="text-text-primary">Warenkorb</span>
        </p>
        <p
          className="font-semibold text-h-feature text-text-primary"
          style={{ fontFamily: "var(--font-lora)" }}
        >
          Warenkorb
        </p>
        <p className="text-body text-text-muted">
          {productsLoading
            ? "Einen Moment…"
            : items.length > 0
              ? "Schön, dass du da bist."
              : "Dein Warenkorb ist noch leer."}
        </p>
      </Reveal>

      {productsLoading ? null : items.length === 0 ? (
        <Reveal className="flex flex-col gap-6 items-start pb-16 pt-2 px-[var(--layout-padding-x)] w-full">
          <p className="text-body text-text-body">
            Schau dir unsere Produkte an und finde, was zu dir passt.
          </p>
          <Link
            href="/shop"
            className="flex gap-2 items-center text-text-primary hover:text-brand transition-colors"
          >
            <ArrowLeftIcon />
            <span className="font-bold text-body-sm">Weiter einkaufen</span>
          </Link>
        </Reveal>
      ) : (
        <>
        <div className="pt-2 px-[var(--layout-padding-x)] w-full">
          {/* threshold forced to null (banner just doesn't render, see its
              own early return) whenever nothing in the cart actually
              triggers shipping — a "€X bis kostenlosem Versand" nudge
              makes no sense for a cart that was never going to be charged
              shipping in the first place. */}
          <FreeShippingBanner subtotal={subtotal} threshold={cartHasShippableItem(items) ? freeShippingThreshold : null} />
        </div>
        <div className="flex flex-col lg:flex-row gap-8 lg:gap-10 items-start pb-10 pt-4 px-[var(--layout-padding-x)] w-full">
          {/* Cart card — lg:-only split from the sidebar (same "wide content
              next to sidebar" shape as the Hero's image/text split, see
              figma-to-nextjs skill Gotcha #5: Figma's 830px card alone
              already exceeds even the site's 640px structural floor, so
              sm: would never have had room for a real 2-column layout at
              Tablet widths anyway — a deliberate exception to the site-wide
              sm: consolidation, not a leftover of it). */}
          <Reveal className="w-full lg:flex-1 flex flex-col gap-6 items-start bg-bg-base border border-border rounded-md p-6 md:p-8">
            {items.map(({ entry, product }, i) => {
              const discount = discountPercent(product.price, product.compareAtPrice);
              const unitPrice = effectivePrice(entry, product);
              const taxRate = effectiveTaxRate(product, defaultTaxRate);
              // (id, variant) together, not id alone — two lines for the
              // same product with different variants need distinct React
              // keys/element ids and must each only affect their own line
              // when the quantity or remove control is used, same "full
              // key" reasoning as cart.ts's own sameLine().
              const lineKey = entry.variant ? `${product.id}::${entry.variant}` : product.id;
              // The exact variant this line is for, not "any variant low"
              // like the product-grid cards use — a cart line already has
              // its variant chosen, so it should only warn when that
              // specific variant (not some other one) is running low.
              const lowStock = entry.variant
                ? (product.variants.find((v) => v.name === entry.variant)?.lowStock ?? false)
                : product.lowStock;
              // Same per-line resolution as lowStock above — caps how high
              // the quantity stepper below can go, instead of only finding
              // out at checkout that this many aren't actually available
              // (api/checkout/route.ts's own stock check stays as the
              // authoritative server-side guard). null (no cap) falls back
              // to the stepper's original fixed 1-9 range; at least 1 is
              // always offered even if maxQty is somehow lower than the
              // qty already in this line, so the remove (×) button stays
              // the only way down, never an empty <select>.
              const maxQty = entry.variant
                ? (product.variants.find((v) => v.name === entry.variant)?.maxQty ?? null)
                : product.maxQty;
              const qtyOptions = Array.from({ length: Math.max(1, Math.min(9, maxQty ?? 9)) }, (_, n) => n + 1);
              return (
              <div key={lineKey} className="w-full">
                {i > 0 && <div className="h-px bg-border w-full mb-6" />}
                <div className="flex flex-col sm:flex-row gap-4 sm:gap-6 items-start sm:items-center w-full">
                  {/* Full-width on mobile (stacked layout) instead of the
                      fixed 150px square — a small square floating above
                      the text looked cramped on a narrow column that has
                      the width to spare; fixed 150px square again from
                      sm: once the row layout kicks in and the image sits
                      beside the text instead. */}
                  <div className="relative w-full aspect-square sm:size-[9.375rem] sm:shrink-0 rounded-sm overflow-hidden">
                    {product.href ? (
                      <Link href={product.href} aria-label={product.name} className="block w-full h-full">
                        <Image
                          src={product.image}
                          alt={product.name}
                          fill
                          sizes="(min-width: 640px) 150px, 100vw"
                          className="object-cover"
                        />
                      </Link>
                    ) : (
                      <Image
                        src={product.image}
                        alt={product.name}
                        fill
                        sizes="(min-width: 640px) 150px, 100vw"
                        className="object-cover"
                      />
                    )}
                    {discount !== null && (
                      <span className="absolute top-2 left-2 rounded-full bg-brand px-2 py-0.5 text-label font-bold text-text-primary">
                        -{discount}%
                      </span>
                    )}
                  </div>
                  <div className="flex flex-col gap-[0.625rem] items-start flex-1 min-w-0 w-full">
                    {product.href ? (
                      <Link
                        href={product.href}
                        className="font-semibold text-h-small text-text-primary hover:text-brand transition-colors"
                        style={{ fontFamily: "var(--font-lora)" }}
                      >
                        <ProductName name={product.name} />
                        {entry.variant ? ` (${entry.variant})` : ""}
                      </Link>
                    ) : (
                      <p
                        className="font-semibold text-h-small text-text-primary"
                        style={{ fontFamily: "var(--font-lora)" }}
                      >
                        <ProductName name={product.name} />
                        {entry.variant ? ` (${entry.variant})` : ""}
                      </p>
                    )}
                    {product.subline && <p className="text-body-sm text-text-muted">{product.subline}</p>}
                    {/* Independent stacked rows, not grid siblings — no
                        equal-height pressure from neighboring lines, so a
                        plain conditional line is enough here. */}
                    {lowStock && <p className="text-label font-bold text-warning">Nur noch wenige verfügbar</p>}
                    <div className="flex flex-col gap-0.5 items-start">
                      <p className="text-label text-text-muted">Einzelpreis</p>
                      <p className="flex items-baseline gap-1.5">
                        {discount !== null && (
                          <span className="text-label text-text-muted line-through">{formatPrice(product.compareAtPrice!)}</span>
                        )}
                        <span className="font-bold text-body-sm text-text-primary">{formatPrice(unitPrice)}</span>
                        {!kleinunternehmer && <span className="text-label text-text-muted">inkl. {taxRate}% MwSt.</span>}
                      </p>
                    </div>
                  </div>
                  <div className="flex gap-4 items-center shrink-0 w-full sm:w-auto justify-between sm:justify-end">
                    <label className="sr-only" htmlFor={`qty-${lineKey}`}>
                      Menge für {product.name}
                      {entry.variant ? ` (${entry.variant})` : ""}
                    </label>
                    <select
                      id={`qty-${lineKey}`}
                      value={entry.qty}
                      onChange={(e) => setQuantity(product.id, Number(e.target.value), entry.variant)}
                      className="border border-border rounded-sm px-3.5 py-2 text-body-sm text-text-primary outline-none focus:border-brand transition-colors"
                    >
                      {qtyOptions.map((n) => (
                        <option key={n} value={n}>{n}</option>
                      ))}
                    </select>
                    <p className="font-bold text-h4 text-text-primary whitespace-nowrap">
                      {formatPrice(entry.qty * unitPrice)}
                    </p>
                    <button
                      type="button"
                      onClick={() => removeFromCart(product.id, entry.variant)}
                      aria-label={`${product.name}${entry.variant ? ` (${entry.variant})` : ""} entfernen`}
                      className="text-text-muted hover:text-text-primary text-xl leading-none active:scale-90 transition-all"
                    >
                      ×
                    </button>
                  </div>
                </div>
              </div>
              );
            })}

            <div className="h-px bg-border w-full" />

            <Link
              href="/shop"
              className="flex gap-2 items-center text-text-primary hover:text-brand transition-colors"
            >
              <ArrowLeftIcon />
              <span className="font-bold text-body-sm">Weiter einkaufen</span>
            </Link>
          </Reveal>

          {/* Sidebar */}
          <Reveal delay={0.1} className="w-full lg:w-[20.625rem] lg:shrink-0 flex flex-col gap-6 items-start">
            <div className="bg-bg-base border border-border rounded-md p-7 flex flex-col gap-5 items-start w-full">
              <p
                className="font-semibold text-h-small text-text-primary"
                style={{ fontFamily: "var(--font-lora)" }}
              >
                Bestellübersicht
              </p>

              <div className="flex items-center w-full">
                <span className="text-body-sm text-text-primary">Zwischensumme</span>
                <span className="flex-1" />
                <span className="text-body-sm text-text-primary">{formatPrice(subtotal)}</span>
              </div>

              {totalSavings > 0 && (
                <div className="flex items-center w-full">
                  <span className="text-body-sm text-success">Du sparst</span>
                  <span className="flex-1" />
                  <span className="font-bold text-body-sm text-success">-{formatPrice(totalSavings)}</span>
                </div>
              )}

              {/* Rabattcode — manual input when nothing's applied yet AND
                  Payload actually has at least one active code right now
                  (showDiscountField — no point offering an open field
                  that could never validate against anything); once
                  active, always shows the result + "Entfernen" regardless
                  of showDiscountField (also reached via a direct link
                  with a prefilled code, see the useEffect above).
                  /checkout mirrors this exact block, sharing state
                  through lib/discount.ts's localStorage store. */}
              {discount ? (
                <div className="flex flex-col gap-2 w-full">
                  <div className="flex items-center w-full">
                    <span className="text-body-sm text-success">Rabattcode ({discount.code})</span>
                    <span className="flex-1" />
                    <span className="font-bold text-body-sm text-success">-{formatPrice(discountAmount)}</span>
                  </div>
                  <button
                    type="button"
                    onClick={clearDiscount}
                    className="self-start text-label text-text-muted hover:text-text-primary underline transition-colors"
                  >
                    Entfernen
                  </button>
                </div>
              ) : showDiscountField ? (
                <form
                  onSubmit={(e) => {
                    e.preventDefault();
                    handleApplyDiscount(discountInput.trim());
                  }}
                  className="flex flex-col gap-2 w-full"
                >
                  <div className="flex gap-2 w-full">
                    <label className="sr-only" htmlFor="cart-discount-code">Rabattcode</label>
                    <input
                      id="cart-discount-code"
                      type="text"
                      value={discountInput}
                      onChange={(e) => setDiscountInput(e.target.value)}
                      placeholder="Rabattcode"
                      className="flex-1 min-w-0 border border-border rounded-sm px-3.5 py-2 text-body-sm text-text-primary outline-none focus:border-brand transition-colors"
                    />
                    <button
                      type="submit"
                      disabled={!discountInput.trim() || discountLoading}
                      className="shrink-0 rounded-sm border border-border px-4 py-2 text-body-sm font-bold text-text-primary hover:border-brand hover:text-brand transition-colors disabled:opacity-50"
                    >
                      Anwenden
                    </button>
                  </div>
                  {discountError && <p className="text-label text-red-600">{discountError}</p>}
                  {discountLoading && <p className="text-label text-text-muted">Rabattcode wird geprüft…</p>}
                </form>
              ) : (
                // No manual field to attach an error to (no active codes
                // exist at all right now) — but a ?code= URL param can
                // still trigger the auto-apply attempt above regardless
                // of showDiscountField, so its failure needs somewhere to
                // show.
                <>
                  {discountError && <p className="text-label text-red-600 w-full">{discountError}</p>}
                  {discountLoading && <p className="text-label text-text-muted w-full">Rabattcode wird geprüft…</p>}
                </>
              )}

              <div className="flex flex-col gap-0.5 w-full">
                {/* Hidden entirely (not just "Kostenlos") when nothing in
                    the cart actually triggers shipping at all — that's a
                    different state from hitting the free-shipping
                    threshold, which is still a real promotional message
                    worth showing. */}
                {cartHasShippableItem(items) && (
                  <>
                    <div className="flex items-center w-full">
                      <span className="flex items-center gap-1.5 text-body-sm text-text-primary">
                        Versand
                        <button
                          type="button"
                          onClick={() => setVersandOpen(true)}
                          className="text-text-muted hover:text-brand transition-colors"
                          aria-label="Alle Informationen zu Versandkosten und Lieferzeiten"
                        >
                          <span aria-hidden>ⓘ</span>
                        </button>
                      </span>
                      <span className="flex-1" />
                      <span className="text-body-sm text-text-primary">
                        {shipping === 0 ? "Kostenlos" : formatPrice(shipping)}
                      </span>
                    </div>
                    <p className="text-label text-text-muted">
                      {shipping === 0 && freeShippingThreshold !== null
                        ? `ab ${formatPrice(freeShippingThreshold)} innerhalb Deutschlands`
                        : "innerhalb Deutschlands"}
                    </p>
                    <p className="text-label text-text-muted">
                      Lieferzeit {shippingSettings.totalDays.min}–{shippingSettings.totalDays.max} Werktage
                    </p>
                  </>
                )}
              </div>

              <div className="h-px bg-border w-full" />

              <div className="flex flex-col gap-0.5 w-full">
                <div className="flex items-center w-full">
                  <span
                    className="font-semibold text-h4 text-text-primary"
                    style={{ fontFamily: "var(--font-lora)" }}
                  >
                    Gesamtsumme
                  </span>
                  <span className="flex-1" />
                  <span className="font-bold text-h-small text-text-primary">{formatPrice(total)}</span>
                </div>
                {kleinunternehmer ? (
                  <p className="text-label text-text-muted">Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.</p>
                ) : (
                  <VatBreakdown groups={taxBreakdown} />
                )}
              </div>

              <Link
                href="/checkout"
                className="w-full flex items-center justify-center py-4 rounded-sm bg-brand hover:bg-brand-hover active:scale-[0.97] transition-all font-bold text-body text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base"
              >
                Zur Kasse gehen
              </Link>

              {/* "Sichere SSL-Verschlüsselung", not "Sichere Zahlung" (what
                  used to be here) — matches /checkout's identical note
                  under its own buy button; the old wording duplicated the
                  "Sichere Zahlung" trustBadges entry right below. */}
              <div className="flex gap-2 items-center justify-center w-full">
                <Image alt="" src="/icon-lock.svg" width={14} height={16} className="w-3.5 h-4" />
                <span className="text-body-sm text-text-muted">Sichere SSL-Verschlüsselung</span>
              </div>
            </div>

            {/* Title only — /checkout renders the same CartTrustBadges
                docs with description too, see CheckoutContent.tsx.
                Side by side between sm (640px) and lg (1024px) — this
                sidebar is full page width through that whole range (it
                only becomes a narrow fixed-width column at lg:, see this
                file's own lg:w-[20.625rem] above), plenty of room for 3
                short titles in a row; stacked at true mobile and again
                once the sidebar narrows at lg:. sm:w-auto with no
                flex-grow (was sm:flex-1) so each badge only takes its own
                content width instead of stretching to fill the full card
                width; sm:justify-center centers that shrink-to-fit row so
                the leftover space splits evenly left/right instead of
                collecting on the right (the default with items packed
                from the start edge). */}
            <div className="flex flex-col sm:flex-row sm:flex-wrap lg:flex-col gap-4 sm:gap-x-8 sm:gap-y-4 lg:gap-4 items-start sm:justify-center lg:justify-start w-full">
              {trustBadges.map((b) => (
                <div key={b.id} className="flex gap-3 items-center w-full sm:w-auto lg:w-full">
                  <Image alt="" src={b.icon} width={22} height={22} className="size-[1.375rem] shrink-0 object-contain" />
                  <span className="flex-1 text-body-sm text-text-primary">{b.title}</span>
                </div>
              ))}
            </div>
          </Reveal>
        </div>
        </>
      )}

      {items.length > 0 && (
        <Reveal className="flex flex-col items-start pb-10 px-[var(--layout-padding-x)] w-full">
          <div className="bg-bg-muted flex gap-5 items-start p-6 rounded-md w-full lg:max-w-[51.875rem]">
            <div className="flex flex-col gap-3 items-center justify-center shrink-0">
              <Image alt="" src="/icon-envelope-hint.png" width={64} height={45} className="h-[2.8125rem] w-16 object-contain" />
              <div className="h-[0.1875rem] w-6 bg-brand" />
            </div>
            <div className="flex flex-col gap-2 items-start flex-1 min-w-0 text-text-primary">
              {/* Framed as an upcoming option, not a given — the real
                  opt-in checkbox lives on /checkout (unchecked by
                  default), and this page doesn't show one at all, so
                  stating regular emails as an already-decided outcome
                  here would overstate consent that hasn't been given
                  yet (UWG/DSGVO — pre-supposed opt-in is unlawful, same
                  reasoning as /checkout's own newsletter hint). */}
              <p className="text-body-sm leading-[1.45]">
                Beim Checkout kannst du dich für unsere Impulse & Tipps per E-Mail anmelden.
              </p>
              <p className="text-body-sm leading-[1.45]">
                Für mehr Klarheit, Fokus und Struktur – jede Woche.
              </p>
            </div>
          </div>
        </Reveal>
      )}

      <VersandModal
        open={versandOpen}
        onClose={() => setVersandOpen(false)}
        shipping={shippingSettings}
        shippingCost={shippingCost}
        freeShippingThreshold={freeShippingThreshold}
      />
    </>
  );
}
