/* ============================================================
   LivePizza — escena viva: spin idle + drag-to-spin,
   toppings con spring, mitad y mitad, morph de tamaño.
   ============================================================ */

/* deterministic RNG seeded from a string */
function phSeed(str) {
  let s = 2166136261 >>> 0;
  for (let i = 0; i < str.length; i++) { s ^= str.charCodeAt(i); s = Math.imul(s, 16777619) >>> 0; }
  return () => { s = (Math.imul(s, 1664525) + 1013904223) >>> 0; return s / 4294967296; };
}

/* scatter a cluster of N toppings for an ingredient inside the pizza disc */
function phCluster(id, n) {
  const rnd = phSeed(id);
  const arr = [];
  for (let i = 0; i < n; i++) {
    const ang = rnd() * Math.PI * 2;
    const rad = 12 + rnd() * 31;               /* % from center, stays off the crust */
    const x = 50 + Math.cos(ang) * rad;
    const y = 50 + Math.sin(ang) * rad;
    const s = 7.5 + rnd() * 4.5;
    arr.push({ x: +x.toFixed(1), y: +y.toFixed(1), s: +s.toFixed(1) });
  }
  return arr;
}

/* cache clusters per ingredient id so positions are stable across renders */
const PH_CLUSTERS = (function () {
  const map = {};
  (window.PHData.INGREDIENTS || []).forEach(ing => { map[ing.id] = phCluster(ing.id, 6); });
  return map;
})();

const PH_ING_CLASS = (function () {
  const m = {};
  (window.PHData.INGREDIENTS || []).forEach(ing => { m[ing.id] = ing.c; });
  return m;
})();

/* size id -> stage fill % (drives the morph) — tuned so the pizza edge lands on its ring */
const PH_SIZE_FILL = { ind: 93, med: 115, fam: 138 };

/* ---------- topping layer for ONE-pizza recipe mode ---------- */
function RecipeToppings({ activeIngs, doughId }) {
  const D = window.PHData;
  return (
    <div className="pizza lp-base" data-dough={doughId}>
      {D.INGREDIENTS.map(ing => {
        const on = activeIngs.includes(ing.id);
        return PH_CLUSTERS[ing.id].map((t, i) => (
          <span
            key={ing.id + i}
            className={"topping " + ing.c + (on ? " on" : "")}
            style={{
              left: t.x + "%", top: t.y + "%", width: t.s + "%", height: t.s + "%",
              transitionDelay: (on ? i * 0.045 : 0) + "s",
            }}
          />
        ));
      })}
    </div>
  );
}

/* ---------- topping layer for a half (uses a pizza preset's toppings) ---------- */
function HalfToppings({ kind, doughId }) {
  const T = window.PHData.T[kind] || window.PHData.T.pepperoni;
  return (
    <div className="pizza lp-base" data-dough={doughId}>
      {T.map((t, i) => (
        <span
          key={i}
          className={"topping " + t.c + " on"}
          style={{ left: t.x + "%", top: t.y + "%", width: t.s + "%", height: t.s + "%" }}
        />
      ))}
    </div>
  );
}

/* ---------- a half "slot": old slides down & out while new enters from the top ---------- */
function HalfSlot({ photo }) {
  const idRef = useRef(0);
  const prevPhoto = useRef(photo);
  const [layers, setLayers] = useState([{ id: 0, photo, cls: "" }]);

  useEffect(() => {
    if (photo === prevPhoto.current) return;
    prevPhoto.current = photo;
    idRef.current += 1;
    const newId = idRef.current;
    setLayers(prev => [
      ...prev.map(l => ({ ...l, cls: "slide-out" })),
      { id: newId, photo, cls: "slide-in" },
    ]);
    const t = setTimeout(() => setLayers(prev => prev.filter(l => l.id === newId || l.cls !== "slide-out")), 700);
    return () => clearTimeout(t);
  }, [photo]);

  const clearOut = () => setLayers(prev => prev.filter(l => l.cls !== "slide-out"));

  return (
    <div className="lp-half-stack">
      {layers.map(l => (
        <div key={l.id} className={"half-inner " + l.cls}
          onAnimationEnd={l.cls === "slide-out" ? clearOut : undefined}>
          <img className="half-img" src={window.__asset(l.photo)} alt="" />
        </div>
      ))}
    </div>
  );
}

/* ============================================================
   Topping drop — real ingredient pieces rain from above & land
   scattered on the pizza. Add an entry per ingredient id below:
     png  → PNG asset
     size → piece width as % of the pizza box (smaller = smaller piece)
     n    → how many pieces scatter onto the pie
   ============================================================ */
const TOPPING_CFG = {
  bacon:   { png: "assets/img-png/topping-bacon.png",   size: 4,   n: 6, spread: 22 },
  tomate:  { png: "assets/img-png/topping-tomate.png",  size: 4.5, n: 7, spread: 28 },
  pollo:   { png: "assets/img-png/topping-pollo.png",   size: 5.5, n: 8, spread: 28 },
  ternera: { png: "assets/img-png/topping-ternera.png", size: 5.5, n: 8, spread: 28 },
  pep:     { png: "assets/img-png/topping-pep.png",     size: 6,   n: 7, spread: 28 },
  champi:  { png: "assets/img-png/topping-champi.png",  size: 5,   n: 6, spread: 28 },
  jamon:   { png: "assets/img-png/topping-jamon.png",   size: 6,   n: 6, spread: 28 },
  aceituna:{ png: "assets/img-png/topping-aceituna.png",size: 3.2, n: 7, spread: 30 },
  /* pimiento: 7 distinct pieces → each scattered piece picks a random variant */
  pimiento:{ png: [1,2,3,4,5,6,7].map(i => "assets/img-png/topping-pimiento-" + i + ".png"),
             size: 3.2, n: 16, spread: 27 },
  /* cebolla: 3 distinct curved strips → random variant per piece */
  cebolla: { png: [1,2,3].map(i => "assets/img-png/topping-cebolla-" + i + ".png"),
             size: 3.2, n: 16, spread: 27 },
};

/* per-ingredient scatter: deterministic (stable across renders) yet DIFFERENT
   for every ingredient — seeded from the id so bacon, tomate, etc. each land on
   their own spots instead of stacking on a shared template. `spread` caps how
   far from center a piece can sit so it ALWAYS rests on the pizza (never on the
   plate). Min spacing scales with piece size so small toppings can pack densely.
   When an ingredient has multiple PNG variants, each piece gets a random one. */
function tpScatter(id, n, spread, size, variants) {
  const rnd = phSeed("tp-" + id);
  const cx = 50, cy = 53;                           /* photo sits ~4% low → match it */
  const pts = [];
  let attempts = 0;
  let minD = Math.max(6, Math.min(20, size * 1.8)); /* spacing ∝ piece size */
  while (pts.length < n && attempts < 1200) {
    attempts++;
    if (attempts % 120 === 0) minD = Math.max(4, minD - 2);  /* relax if crowded */
    const ang = rnd() * Math.PI * 2;
    const rad = 3 + rnd() * (spread - 3);            /* 3%..spread% from center */
    const x = cx + Math.cos(ang) * rad;
    const y = cy + Math.sin(ang) * rad;
    if (pts.some(p => Math.hypot(p.x - x, p.y - y) < minD)) continue;
    pts.push({
      x: +x.toFixed(1), y: +y.toFixed(1),
      r: Math.round((rnd() * 2 - 1) * 90),          /* resting rotation */
      s: +(0.82 + rnd() * 0.4).toFixed(2),          /* per-piece size jitter */
      oa: Math.round(rnd() * 360 - 180),            /* window edge it flies in from */
      v: variants > 1 ? Math.floor(rnd() * variants) : 0,  /* PNG variant index */
    });
  }
  return pts;
}

/* cache one scatter per ingredient id */
const TP_SCATTERS = (function () {
  const m = {};
  Object.keys(TOPPING_CFG).forEach(id => {
    const c = TOPPING_CFG[id];
    m[id] = tpScatter(id, c.n, c.spread, c.size, Array.isArray(c.png) ? c.png.length : 1);
  });
  return m;
})();

function ToppingDrops({ activeIngs = [], stageRef, sizeId = "med" }) {
  const withPng = activeIngs.filter(id => TOPPING_CFG[id]);
  const [groups, setGroups] = useState(() => withPng.map(id => ({ id, status: "in" })));
  const prev = useRef(withPng);

  /* track the pizza stage's on-screen box so the portal overlay (rendered at
     the top of the page, above ALL chrome) lands exactly on the pizza */
  const [rect, setRect] = useState(null);
  useEffect(() => {
    const el = stageRef && stageRef.current;
    if (!el) return;
    const measure = () => {
      const r = el.getBoundingClientRect();
      setRect({ left: r.left, top: r.top, width: r.width, height: r.height });
    };
    measure();
    window.addEventListener("scroll", measure, true);
    window.addEventListener("resize", measure);
    const ro = (typeof ResizeObserver !== "undefined") ? new ResizeObserver(measure) : null;
    if (ro) ro.observe(el);
    return () => {
      window.removeEventListener("scroll", measure, true);
      window.removeEventListener("resize", measure);
      if (ro) ro.disconnect();
    };
  }, [stageRef]);

  useEffect(() => {
    const prevIds = prev.current;
    const cur = withPng;
    const added = cur.filter(id => !prevIds.includes(id));
    const removed = prevIds.filter(id => !cur.includes(id));
    if (added.length || removed.length) {
      setGroups(gs => {
        let next = gs.map(g => removed.includes(g.id) ? { ...g, status: "out" } : g);
        added.forEach(id => {
          if (!next.find(g => g.id === id && g.status === "in")) next = [...next, { id, status: "in" }];
        });
        return next;
      });
    }
    prev.current = cur;
  }, [activeIngs.join(",")]);

  const clearOut = (id) => setGroups(gs => gs.filter(g => !(g.id === id && g.status === "out")));

  /* distance each piece flies in from — reaches past the window edge */
  const vw = (typeof window !== "undefined" ? window.innerWidth : 1280);
  const vh = (typeof window !== "undefined" ? window.innerHeight : 800);
  const D = Math.max(vw, vh) * 0.62;

  /* match the pizza's size-morph scale so resting pieces stay on the pie */
  const scale = (PH_SIZE_FILL[sizeId] || 100) / 100;

  if (!rect || typeof ReactDOM === "undefined" || !ReactDOM.createPortal) return null;

  const pieces = (
    <div className="tp-overlay"
      style={{ left: rect.left + "px", top: rect.top + "px", width: rect.width + "px", height: rect.height + "px" }}>
      <div className="tp-layer" style={{ transform: `scale(${scale})` }}>
        {groups.map(g => {
          const cfg = TOPPING_CFG[g.id];
          const scatter = TP_SCATTERS[g.id] || [];
          return (
          <div className="tp-group" key={g.id + "-" + g.status}>
          {scatter.map((p, i) => {
            const a = (p.oa * Math.PI) / 180;
            const dx = Math.round(Math.cos(a) * D);
            const dy = Math.round(Math.sin(a) * D);
            return (
              <span key={i}
                className={"tp-piece " + (g.status === "out" ? "out" : "in")}
                style={{
                  left: p.x + "%", top: p.y + "%", width: cfg.size + "%",
                  "--r": p.r + "deg", "--s": p.s,
                  "--dx": dx + "px", "--dy": dy + "px",
                  animationDelay: (g.status === "out" ? i * 26 : i * 72) + "ms",
                }}
                onAnimationEnd={(g.status === "out" && i === scatter.length - 1) ? () => clearOut(g.id) : undefined}>
                <img src={window.__asset(Array.isArray(cfg.png) ? cfg.png[p.v % cfg.png.length] : cfg.png)} alt="" draggable="false" />
              </span>
            );
          })}
        </div>
          );
        })}
      </div>
    </div>
  );

  return ReactDOM.createPortal(pieces, document.body);
}

/* ============================================================
   LivePizza
   props:
     mode: "one" | "half"
     activeIngs, doughId            (one mode)
     leftKind, rightKind            (half mode)
     sizeId
     motion                         (0..1.6 — idle spin speed)
     sweepKey                       (changes to retrigger the half sweep)
   ============================================================ */
function LivePizza({ mode, sizeId, motion = 1, photo, leftPhoto, rightPhoto, activeIngs = [] }) {
  const spinRef = useRef(null);
  const stageRef = useRef(null);
  const state = useRef({ angle: 0, vel: 0, dragging: false, lastA: 0, raf: 0 });
  const [grabbing, setGrabbing] = useState(false);

  const fill = PH_SIZE_FILL[sizeId] || 80;
  const sizeRef = useRef(null);
  const prevFill = useRef(fill);

  /* size morph via WAAPI — independent of React re-renders (count-up etc.) */
  useEffect(() => {
    const el = sizeRef.current;
    const from = prevFill.current / 100, to = fill / 100;
    prevFill.current = fill;
    if (!el || from === to) return;
    el.animate(
      [
        { transform: `translate(-50%, -50%) scale(${from})` },
        { transform: `translate(-50%, -50%) scale(${to})` },
      ],
      { duration: 580, easing: "cubic-bezier(.34, 1.62, .56, 1)", fill: "backwards" }
    );
  }, [fill]);

  /* rAF loop: keeps inertia after a drag, but NO idle auto-spin */
  useEffect(() => {
    const st = state.current;
    const tick = () => {
      if (!st.dragging && Math.abs(st.vel) > 0.04) { st.angle += st.vel; st.vel *= 0.95; }
      if (spinRef.current) spinRef.current.style.transform = "rotate(" + st.angle + "deg)";
      st.raf = requestAnimationFrame(tick);
    };
    st.raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(st.raf);
  }, [motion]);

  return (
    <div className="stage" ref={stageRef}>
      <div className="size-rings">
        <span className={"sring r-fam" + (sizeId === "fam" ? " active" : "")} />
        <span className={"sring r-med" + (sizeId === "med" ? " active" : "")} />
        <span className={"sring r-ind" + (sizeId === "ind" ? " active" : "")} />
      </div>
      <div className="stage-glow" />
      <div className="steam"><i /><i /><i /></div>

      <div className="lp-size" ref={sizeRef} style={{ transform: `translate(-50%, -50%) scale(${fill / 100})` }}>
        {mode === "one" ? (
          <div className="lp-one">
            <div className="lp-photo-disc" key={photo}>
              <img src={window.__asset(photo)} alt="" />
            </div>
            <ToppingDrops activeIngs={activeIngs} stageRef={stageRef} sizeId={sizeId} />
          </div>
        ) : (
          <div className="lp-spin" ref={spinRef}>
            <div className="lp-half l"><HalfSlot photo={leftPhoto} /></div>
            <div className="lp-half r"><HalfSlot photo={rightPhoto} /></div>
            <div className="lp-divider" />
          </div>
        )}
      </div>

      <div className="diam-badge">
        <span className="db-ico"><Icon name="pizza" size={15} /></span>
        <span className="db-num">{(window.PHData.SIZES.find(s => s.id === sizeId) || {}).diam || ""}</span>
      </div>
    </div>
  );
}

Object.assign(window, { LivePizza, RecipeToppings, HalfToppings, PH_CLUSTERS, PH_SIZE_FILL });
