import Link from "next/link";
import Image from "next/image";
import type { Product, ProductBlock, PageBlock } from "../lib/payload";
import {
  getShippingSettings,
  getDefaultTaxRatePercent,
  getKleinunternehmer,
  getTestimonials,
  getProducts,
  getProductsByIds,
  getWishlistEnabled,
} from "../lib/payload";
import { formatPrice, discountPercent } from "../lib/format";
import { effectiveTaxRate } from "../lib/cartTotals";
import { AddToCartButton } from "./AddToCartButton";
import { ProductName } from "./ProductName";
import { RichText } from "./RichText";
import { Reveal } from "./Reveal";
import { ProductGallery } from "./ProductGallery";
import { ProductCard } from "./ProductCard";
import { STEP_ICONS } from "./icons/StepIcons";
import { renderPageBlockSync } from "./PageBlocks";
import { TestimonialsGrid } from "./TestimonialsGrid";

// Same 3-recommendation width as cart's RelatedProducts.tsx grid, minus
// its cart-awareness (a PDP cross-sell doesn't need to exclude/reshuffle
// around what's already in the cart, that logic is specific to the cart
// page's own "here's what's missing" framing).
const CROSS_SELL_COUNT = 3;

// Renders a Payload Products document's `layout` blocks field — same
// "page sections, not inline Lexical nodes" shape as PageBlocks.tsx, which
// this delegates to directly for every block type the two fields share
// (richTextSection/stepRow/quote/image/icon/pillList/checklistImage/table/
// ctaCard are the exact same Block configs, just registered a second time
// on Products.layout — see that field's own comment in Products.ts). Only
// `productHero` and `productPricingPanel` are product-specific, since they
// need the live product/shipping/tax context that a generic content page
// doesn't have.
export async function ProductBlocks({ product }: { product: Product }) {
  const [shipping, defaultTaxRate, kleinunternehmer, wishlistEnabled] = await Promise.all([
    getShippingSettings(),
    getDefaultTaxRatePercent(),
    getKleinunternehmer(),
    getWishlistEnabled(),
  ]);
  const ctx: ProductBlockContext = { product, shipping, taxRate: effectiveTaxRate(product, defaultTaxRate), kleinunternehmer };

  const rendered = await Promise.all(
    product.layout.map(async (block) => {
      // Needs its own async data fetch — same reason PageBlocks.tsx
      // handles this one block type in its own outer async map instead of
      // the sync switch below (renderProductBlock/renderPageBlockSync).
      if (block.blockType === "testimonialsRef") {
        const testimonials = await getTestimonials(block.page);
        if (testimonials.length === 0) return null;
        return (
          <div key={block.id} className="w-full px-[var(--layout-padding-x)] py-6">
            <TestimonialsGrid testimonials={testimonials} />
          </div>
        );
      }
      // Also needs its own async fetch — same reasoning as testimonialsRef
      // just above. `manual` resolves the block's own picks by id;
      // `automatic` picks active products sharing a category with this
      // one (falling back to any other active product if it has none),
      // excluding itself either way.
      if (block.blockType === "crossSell") {
        const recommended = await getCrossSellProducts(block, product);
        if (recommended.length === 0) return null;
        return (
          <div key={block.id} className="w-full px-[var(--layout-padding-x)] py-8 sm:py-12">
            <p className="font-semibold text-h-small text-text-primary mb-6" style={{ fontFamily: "var(--font-lora)" }}>
              Das passt dazu
            </p>
            <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
              {recommended.map((p) => (
                <ProductCard
                  key={p.id}
                  product={p}
                  defaultTaxRate={defaultTaxRate}
                  kleinunternehmer={kleinunternehmer}
                  wishlistEnabled={wishlistEnabled}
                  wishlistRevealOnHover
                />
              ))}
            </div>
          </div>
        );
      }
      // productHero/productPricingPanel render their own full section
      // (edge-to-edge image, own Reveal, own padding rhythm) — matching
      // the hand-coded PDPs (todo-cards etc.) precisely needed each of
      // those, unlike a generic content block, so they opt out of this
      // shared wrapper entirely rather than fighting it.
      if (block.blockType === "productHero" || block.blockType === "productPricingPanel") {
        return <div key={block.id}>{renderProductBlock(block, ctx)}</div>;
      }
      return (
        <Reveal key={block.id} className="w-full px-[var(--layout-padding-x)] py-8 sm:py-12">
          {renderProductBlock(block, ctx)}
        </Reveal>
      );
    })
  );

  return <>{rendered}</>;
}

async function getCrossSellProducts(
  block: Extract<ProductBlock, { blockType: "crossSell" }>,
  current: Product
): Promise<Product[]> {
  if (block.mode === "manual") {
    if (block.productIds.length === 0) return [];
    const picked = await getProductsByIds(block.productIds);
    return picked.filter((p) => p.id !== current.id).slice(0, CROSS_SELL_COUNT);
  }
  const all = await getProducts();
  const active = all.filter((p) => p.active && p.id !== current.id);
  const sameCategory = current.categories.length > 0 ? active.filter((p) => p.categories.some((c) => current.categories.includes(c))) : [];
  const pool = sameCategory.length > 0 ? sameCategory : active;
  return pool.slice(0, CROSS_SELL_COUNT);
}

type ProductBlockContext = {
  product: Product;
  shipping: Awaited<ReturnType<typeof getShippingSettings>>;
  taxRate: number;
  kleinunternehmer: boolean;
};

function renderProductBlock(block: ProductBlock, ctx: ProductBlockContext): React.ReactNode {
  switch (block.blockType) {
    case "productHero":
      return <ProductHero {...ctx} body={block.body} />;
    case "productPricingPanel":
      return <ProductPricingPanel {...ctx} />;
    // Needs an async fetch — handled by ProductBlocks' own outer
    // Promise.all above, same as testimonialsRef. Never actually reached
    // from there since both return their own JSX before calling this.
    case "crossSell":
      return null;
    // A PDP-specific full-bleed treatment for this one block type only —
    // not editing the shared Quote.tsx/renderPageBlockSync, which would
    // also restyle every Pages.layout quote (e.g. /ueber-mich's).
    case "quote":
      return (
        <div key={block.id} className="bg-brand rounded-xl px-6 py-10 sm:px-12 sm:py-14 text-center">
          <p className="font-semibold text-h-small sm:text-h3 text-text-primary leading-snug" style={{ fontFamily: "var(--font-lora)" }}>
            {block.text}
          </p>
        </div>
      );
    // Every other block type is identical to Pages.layout's own — same
    // Block config, reused a second time (see this file's own comment).
    default:
      return renderPageBlockSync(block as PageBlock);
  }
}

// Shared by ProductHero and ProductPricingPanel — previously only the
// pricing panel showed this (discount%/Ausverkauft), the hero showed
// nothing. Priority: out-of-stock, then discount, then the one manual
// state (Products.badge === "new") — never discount+badge at once, one
// pill only. Discount/Ausverkauft are derived, never editor-set, per
// Products.ts's own field comment on why "new" is the only manual option.
function ProductBadge({ product }: { product: Product }) {
  const discount = discountPercent(product.price, product.compareAtPrice);
  const fullyOutOfStock =
    !product.active || (product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock);

  if (fullyOutOfStock) {
    return <span className="rounded-full bg-text-muted px-2.5 py-1 text-label font-bold text-bg-base">Ausverkauft</span>;
  }
  if (discount !== null) {
    return <span className="rounded-full bg-brand px-2.5 py-1 text-label font-bold text-text-primary">-{discount}%</span>;
  }
  if (product.badge === "new") {
    return <span className="rounded-full bg-brand px-2.5 py-1 text-label font-bold text-text-primary">Neu</span>;
  }
  return null;
}

// Matches der-eine/todo-cards' own hand-coded Hero.tsx exactly — same
// section/grid/Reveal/padding structure, same edge-to-edge image with
// hover-scale — so a block-driven PDP hero doesn't visually stand apart
// from its hand-coded siblings. Only the body prose differs: it's this
// block's own `body` richText instead of hardcoded JSX.
function ProductHero({ product, shipping, taxRate, kleinunternehmer, body }: ProductBlockContext & { body: unknown }) {
  const discount = discountPercent(product.price, product.compareAtPrice);
  const fullyOutOfStock =
    !product.active || (product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock);
  const anyLowStock = product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock);

  return (
    <section className="bg-bg-base w-full overflow-hidden">
      <div className="flex flex-col lg:grid lg:grid-cols-12 gap-8 lg:gap-[var(--layout-grid-gap)] pt-10 md:pt-12">
        <Reveal className="order-1 lg:order-none lg:col-span-5 flex flex-col gap-6 items-start pl-[var(--layout-padding-x)] pr-10 lg:pr-0">
          <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>
            <Link href="/#werkzeuge" className="hover:text-brand transition-colors">
              Werkzeuge
            </Link>
            <span>›</span>
            <span className="text-text-primary">
              <ProductName name={product.name} />
            </span>
          </p>

          <div className="flex flex-col gap-6 items-start w-full flex-1 lg:justify-center">
            <div className="flex flex-col gap-2 items-start w-full">
              <p className="font-semibold text-h-page text-text-primary" style={{ fontFamily: "var(--font-playfair)" }}>
                <ProductName name={product.name} />
              </p>
              {product.subline && (
                <p className="font-semibold text-h3 text-text-primary" style={{ fontFamily: "var(--font-lora)" }}>
                  {product.subline}
                </p>
              )}
            </div>

            {body ? <RichText content={body} /> : null}

            {product.usps.length > 0 && (
              <ul className="flex flex-col gap-2 w-full">
                {product.usps.map((usp, i) => {
                  const Icon = STEP_ICONS[usp.icon];
                  return (
                    <li key={i} className="flex items-center gap-3">
                      {Icon && <span className="flex items-center justify-center size-6 shrink-0 text-brand">{Icon()}</span>}
                      <p className="text-body-sm text-text-body">{usp.text}</p>
                    </li>
                  );
                })}
              </ul>
            )}

            <div className="flex flex-col gap-1 items-start">
              <div className="flex gap-2 items-baseline">
                {discount !== null && (
                  <p className="text-body text-text-muted line-through">{formatPrice(product.compareAtPrice!)}</p>
                )}
                <p className="font-bold text-h3 text-text-primary">{formatPrice(product.price)}</p>
                <p className="text-label text-text-muted">
                  {kleinunternehmer
                    ? product.noShippingCost
                      ? "Keine Versandkosten"
                      : "zzgl. Versand"
                    : `inkl. ${taxRate}% MwSt. ${product.noShippingCost ? "– keine Versandkosten" : "zzgl. Versand"}`}
                </p>
              </div>
              {!product.noShippingCost && (
                <p className="text-label text-text-muted">
                  Lieferzeit: {shipping.totalDays.min}–{shipping.totalDays.max} Werktage innerhalb Deutschlands
                </p>
              )}
              {anyLowStock && <p className="text-label font-bold text-warning">Nur noch wenige verfügbar</p>}
            </div>

            <AddToCartButton
              label="In den Warenkorb"
              className="w-full sm:w-auto inline-flex items-center justify-center px-8 py-[0.8125rem] rounded-sm bg-brand hover:bg-brand-hover active:scale-[0.97] transition-all font-bold text-body text-text-primary text-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base"
              numericId={product.numericId}
              outOfStock={fullyOutOfStock}
              maxQty={product.maxQty}
              variants={product.active ? product.variants : []}
              showQuantityStepper
            />
          </div>
        </Reveal>

        <Reveal className="order-2 lg:order-none lg:col-span-7" delay={0.15}>
          <ProductGallery image={product.image} gallery={product.gallery} alt={product.name} badge={<ProductBadge product={product} />} />
        </Reveal>
      </div>
    </section>
  );
}

// Matches todo-cards' own hand-coded Pricing.tsx exactly — see that
// file's own comment on why this closing panel looks the way it does.
function ProductPricingPanel({ product, shipping, taxRate, kleinunternehmer }: ProductBlockContext) {
  const discount = discountPercent(product.price, product.compareAtPrice);
  const fullyOutOfStock =
    !product.active || (product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock);
  const anyLowStock = product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock);

  return (
    <section className="w-full bg-bg-base px-[var(--layout-padding-x)] py-8">
      <Reveal className="bg-bg-muted rounded-md flex flex-col lg:flex-row gap-8 lg:gap-12 items-center p-6 lg:pl-8 lg:pr-10 lg:py-6">
      <div className="group relative w-full lg:w-[25.625rem] lg:shrink-0 aspect-[410/227] rounded-sm overflow-hidden">
        <Image
          src={product.image}
          alt={product.name}
          fill
          sizes="(min-width: 1024px) 410px, 100vw"
          className="object-cover transition-transform duration-500 group-hover:scale-105"
        />
        <div className="absolute top-3 left-3">
          <ProductBadge product={product} />
        </div>
      </div>

      <div className="flex flex-col gap-3 items-start flex-1 min-w-0 w-full">
        <p className="font-semibold text-h-small text-text-primary" style={{ fontFamily: "var(--font-lora)" }}>
          <ProductName name={product.name} />
        </p>
        {product.subline && <p className="text-body-sm text-text-primary">{product.subline}</p>}
      </div>

      <div className="flex flex-col gap-3 items-start w-full lg:w-[18.75rem] lg:shrink-0">
        <div className="flex flex-col gap-1 items-start">
          <div className="flex gap-2 items-baseline">
            {discount !== null && (
              <p className="text-body text-text-muted line-through">{formatPrice(product.compareAtPrice!)}</p>
            )}
            <p className="font-bold text-h3 text-text-primary">{formatPrice(product.price)}</p>
          </div>
          <p className="text-label text-text-muted">
            {kleinunternehmer
              ? product.noShippingCost
                ? "Keine Versandkosten"
                : "zzgl. Versand"
              : `inkl. ${taxRate}% MwSt. ${product.noShippingCost ? "– keine Versandkosten" : "zzgl. Versand"}`}
          </p>
          {!product.noShippingCost && (
            <p className="text-label text-text-muted">
              Lieferzeit: {shipping.totalDays.min}–{shipping.totalDays.max} Werktage innerhalb Deutschlands
            </p>
          )}
        </div>
        {anyLowStock && <p className="text-label font-bold text-warning">Nur noch wenige verfügbar</p>}
        <AddToCartButton
          label="In den Warenkorb"
          className="w-full inline-flex items-center justify-center px-6 py-[0.8125rem] rounded-sm bg-brand hover:bg-brand-hover active:scale-[0.97] transition-all font-bold text-body text-text-primary text-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-muted"
          numericId={product.numericId}
          outOfStock={fullyOutOfStock}
          maxQty={product.maxQty}
          variants={product.active ? product.variants : []}
          showQuantityStepper
        />
      </div>
      </Reveal>
    </section>
  );
}
