// Shared atoms: Icon (Lucide), Button, Badge, money
const { useEffect, useRef, useState } = React;

// Swiss franc formatting: whole amounts as "CHF 14.–", otherwise "CHF 14.50"
function money(n) {
  return Number.isInteger(n) ? `CHF ${n}.–` : `CHF ${n.toFixed(2)}`;
}

// Lucide icon. Renders a placeholder <i> that lucide.createIcons() upgrades to <svg>.
function Icon({ name, size = 20, color, style }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.innerHTML = "";
    const i = document.createElement("i");
    i.setAttribute("data-lucide", name);
    el.appendChild(i);
    if (window.lucide) window.lucide.createIcons();
  }, [name]);
  return <span className="ic" ref={ref} style={{ width: size, height: size, color, ...style }}></span>;
}

function Button({ variant = "primary", size = "md", block, children, onClick, disabled, type }) {
  const cls = `btn btn--${size} btn--${variant}${block ? " btn--block" : ""}`;
  return <button className={cls} onClick={onClick} disabled={disabled} type={type}>{children}</button>;
}

function Badge({ kind, children }) {
  return <span className={`badge badge--${kind}`}>{children}</span>;
}

// Decorative bottle stand-in used inside placeholder media tiles
function Bottle({ color, className = "bottle" }) {
  return <div className={className} style={{ background: color }}></div>;
}

// Scroll-reveal: adds `.in` when the element scrolls into view (once).
function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (!("IntersectionObserver" in window)) { el.classList.add("in"); return; }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } });
    }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return ref;
}

function Reveal({ as = "div", delay, className = "", children, style }) {
  const ref = useReveal();
  const Tag = as;
  const cls = `reveal ${delay ? "reveal-d" + delay : ""} ${className}`.trim();
  return <Tag ref={ref} className={cls} style={style}>{children}</Tag>;
}

// Sprig mark as inline data-URIs so it survives bundling / offline use (no external file fetch)
const _sprigSVG = (s, l, b) => `<svg width="48" height="56" viewBox="0 0 48 56" xmlns="http://www.w3.org/2000/svg"><path d="M24 53 C23 42 24 32 24 22 C24 14 24 9 24 4" stroke="${l}" stroke-width="2.4" stroke-linecap="round" fill="none"/><path d="M24 40 C16 41 11 37 9 30 C17 29 22 33 24 40 Z" fill="${l}"/><path d="M24 40 C32 41 37 37 39 30 C31 29 26 33 24 40 Z" fill="${s}"/><path d="M24 30 C17 30 13 26 11 20 C18 20 22 24 24 30 Z" fill="${s}"/><path d="M24 30 C31 30 35 26 37 20 C30 20 26 24 24 30 Z" fill="${l}"/><path d="M24 19 C19 19 16 16 14 11 C20 11 23 15 24 19 Z" fill="${l}"/><path d="M24 19 C29 19 32 16 34 11 C28 11 25 15 24 19 Z" fill="${s}"/><circle cx="24" cy="5" r="2.6" fill="${b}"/></svg>`;
const SPRIG = "data:image/svg+xml," + encodeURIComponent(_sprigSVG("#748A5A", "#5E7448", "#ED5B2D"));
const SPRIG_LIGHT = "data:image/svg+xml," + encodeURIComponent(_sprigSVG("#93A579", "#B4C09C", "#F4AC92"));

// Apéro Garden wordmark (LOGO.svg, inlined so fill="currentColor" works —
// one source, two looks: brand-deep on light surfaces, green-50 on dark).
// Aspect ratio ~7:1, so size it with CSS height + width:auto via the
// className. Source: public/site/assets/logo.svg.
function Logo({ className, style }) {
  return (
    <svg
      role="img"
      aria-label="Apéro Garden"
      viewBox="0 0 1553.31 222.05"
      xmlns="http://www.w3.org/2000/svg"
      fill="currentColor"
      className={className}
      style={{ width: "auto", display: "block", ...style }}
    >
      <title>Apéro Garden</title>
      <path d="M123.22,121.29H57.16l-13.3,41.82H7.83L53.95,15.35h72.71l45.9,147.77h-36.03l-13.3-41.82ZM67.89,87.62h44.61l-21.66-68.42h-1.07l-21.88,68.42Z"/>
      <path d="M185.85,57.17h32.6l-.21,38.39h.64c6.22-28.1,29.6-41.82,50.61-41.82,24.24,0,48.47,17.8,48.47,55.55s-24.88,55.33-49.54,55.33c-20.59,0-43.32-13.51-49.54-41.61h-.64l1.07,91.79h-33.24l-.21-157.63ZM253.84,136.74c24.02,0,35.6-14.37,35.6-27.45,0-14.8-11.58-27.24-35.6-27.24-22.31,0-34.74,12.44-34.74,27.24s12.44,27.45,34.74,27.45Z"/>
      <path d="M457.93,121.92c-3.97,12.95-15.73,32.43-52.81,40.55-43.37,9.5-69.41-9.17-76.07-39.55-8.21-37.5,19.15-61.94,49.32-68.55,32.47-7.11,69.96,4.66,75.62,49.52l-88.78,19.67c2.66,12.15,19.63,15.46,34.09,12.29,15.08-3.3,21.25-11.24,23.71-17.05l34.93,3.11ZM331.38,52.38l77.3-30.98,2.4,4.96-77.35,30.77-2.36-4.75ZM360.63,103.71l58.24-12.76c-4.25-11.37-20.31-13.56-33.72-10.62-12.78,2.8-25.37,11.49-24.52,23.38Z"/>
      <path d="M471.19,57.17h34.74l-.64,42.68h.64c5.15-19.52,18.02-46.11,51.69-46.11v33.24c-26.38-6.22-52.55,8.36-52.55,25.95l-.86,49.97h-33.03V57.17Z"/>
      <path d="M625.45,53.74c33.24,0,60.05,24.88,60.05,55.55s-26.81,55.55-60.05,55.55-60.05-24.88-60.05-55.55,26.81-55.55,60.05-55.55ZM625.45,133.73c18.66,0,33.67-11.37,33.67-24.45s-15.01-24.02-33.67-24.02-34.53,10.94-34.53,24.02,15.66,24.45,34.53,24.45Z"/>
      <path d="M869.8,77.33c-6.65-12.87-19.3-28.74-49.11-28.74-27.45,0-48.25,14.58-48.25,40.11,0,31.74,29.81,41.82,59.84,41.82,26.81,0,53.62-6.86,62.2-15.44v-.21h-75.49v-32.6h78.71v65.63c-19.73,11.79-42.25,16.94-70.77,16.94-43.97,0-91.15-24.88-91.15-77s43.75-74.64,86-74.64c36.46,0,69.7,17.59,81.71,53.4l-33.67,10.72Z"/>
      <path d="M993.53,162.9v-44.82h-.64c-8.58,25.09-28.09,46.54-51.69,46.54-19.52,0-34.1-14.8-34.1-31.96,0-38.18,60.91-36.89,87.07-46.97v-1.5h-76.14v-27.02h104.88v105.73h-29.38ZM951.71,140.38c12.44,0,30.88-11.15,40.54-25.95v-3c-15.23,5.79-51.9,7.51-51.9,19.52,0,6.01,4.72,9.44,11.36,9.44Z"/>
      <path d="M1035.46,57.17h34.74l-.64,42.68h.64c5.15-19.52,18.02-46.11,51.69-46.11v33.24c-26.38-6.22-52.55,8.36-52.55,25.95l-.86,49.97h-33.03V57.17Z"/>
      <path d="M1261.01,14.27l.21,148.63h-32.6l.21-40.11h-.86c-6.22,28.1-29.38,41.82-50.4,41.82-24.23,0-48.68-17.8-48.68-55.55s24.88-55.33,49.54-55.33c21.23,0,43.32,13.51,49.54,41.61h.86l-1.29-81.07h33.46ZM1193.24,81.62c-24.24,0-35.6,14.37-35.6,27.45,0,14.8,11.36,27.24,35.6,27.24,22.31,0,34.53-12.44,34.53-27.24s-12.22-27.45-34.53-27.45Z"/>
      <path d="M1396.72,136.31c-6.65,11.8-22.31,28.31-60.27,28.31-44.4,0-65.84-23.81-65.84-54.9,0-38.39,31.96-56.41,62.84-56.41,33.24,0,67.35,19.52,63.27,64.56l-90.94.21c0,12.44,15.87,19.3,30.67,19.3s23.16-6.44,26.81-11.58l33.46,10.51ZM1305.57,97.7h59.62c-1.72-12.01-16.94-17.59-30.67-17.59s-27.24,5.79-28.96,17.59Z"/>
      <path d="M1407.98,57.17h34.53l-1.07,40.53h1.07c3.43-20.59,19.3-43.97,53.83-43.97,24.88,0,44.4,15.23,44.4,43.97l.43,65.2h-35.18l-.21-54.69c0-18.01-12.44-28.31-28.95-28.31-24.66,0-34.53,19.95-35.17,33.46l-.86,49.54h-32.81V57.17Z"/>
    </svg>
  );
}

/**
 * Fly-to-cart effect. Clones the product photo at the source element's
 * size + position (typically the product card's media tile) and animates
 * it on a slight arc up toward the cart icon in the header, scaling down
 * and fading out as it lands. Pure visual decoration — cart state has
 * already been updated by the caller before we run.
 *
 * Skipped when:
 *   - prefers-reduced-motion is set
 *   - the cart drawer or search overlay is already open (target is hidden)
 *   - the product has no image (nothing to clone)
 *   - the source or cart icon can't be located
 */
function launchFlyToCart(product, sourceEl) {
  const log = (...a) => console.log("[fly→cart]", ...a);
  if (!product)           { log("skip: no product"); return; }
  if (!sourceEl)          { log("skip: no sourceEl"); return; }
  if (window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) { log("skip: reduced motion"); return; }
  if (document.querySelector(".drawer.open"))  { log("skip: cart drawer open"); return; }
  if (document.querySelector(".search.open"))  { log("skip: search overlay open"); return; }
  const destEl = document.querySelector('[aria-label="Warenkorb"]');
  if (!destEl) { log("skip: no cart icon found"); return; }

  const fromRect = sourceEl.getBoundingClientRect();
  const toRect   = destEl.getBoundingClientRect();
  if (!fromRect.width || !fromRect.height) { log("skip: source has 0 size", fromRect); return; }

  log("launching", { product: product.name, from: fromRect, to: toRect, hasImage: !!product.image });

  const fly = document.createElement("div");
  fly.className = "fly-to-cart";
  // Solid fallback so the fly is visible even if the image is slow / blocked.
  // The actual product photo overlays it once loaded.
  fly.style.background = "var(--brand)";
  if (product.image) fly.style.backgroundImage = `url("${product.image}")`;
  fly.style.left   = fromRect.left + "px";
  fly.style.top    = fromRect.top + "px";
  fly.style.width  = fromRect.width + "px";
  fly.style.height = fromRect.height + "px";
  document.body.appendChild(fly);

  const dx = (toRect.left + toRect.width / 2)  - (fromRect.left + fromRect.width / 2);
  const dy = (toRect.top  + toRect.height / 2) - (fromRect.top  + fromRect.height / 2);
  // Arc height scales with distance so a card right under the header arcs
  // less than one in the lower grid. Capped so it never goes off-screen.
  const distance  = Math.hypot(dx, dy);
  const arcHeight = Math.min(160, Math.max(40, distance * 0.22));

  const anim = fly.animate(
    [
      { transform: "translate(0,0) scale(1) rotate(0deg)", opacity: 1 },
      { transform: `translate(${dx * 0.35}px, ${dy * 0.35 - arcHeight}px) scale(0.5) rotate(6deg)`, opacity: 0.92, offset: 0.4 },
      { transform: `translate(${dx}px, ${dy}px) scale(0.08) rotate(-4deg)`, opacity: 0.2 },
    ],
    { duration: 800, easing: "cubic-bezier(0.45, 0.05, 0.4, 0.95)", fill: "forwards" }
  );
  anim.onfinish   = () => fly.remove();
  anim.oncancel   = () => fly.remove();
}

Object.assign(window, { money, Icon, Button, Badge, Bottle, Logo, useReveal, Reveal, SPRIG, SPRIG_LIGHT, launchFlyToCart });
