/* ============================================================
   Shared components + cart store for the storefront template.
   Loaded before pages.jsx and app.jsx. Exports to window.
   ============================================================ */

const { useState, useEffect, useCallback, useRef } = React;

/* ---- Cart store (localStorage, namespaced per brand) -------- */
function cartKey(brand) { return `cart:${brand}`; }

function useCart(brand, ephemeral = false) {
  const [items, setItems] = useState(() => {
    if (ephemeral) return {};
    try { return JSON.parse(localStorage.getItem(cartKey(brand))) || {}; }
    catch { return {}; }
  });

  const write = (next) => {
    if (ephemeral) return;
    localStorage.setItem(cartKey(brand), JSON.stringify(next));
    window.dispatchEvent(new CustomEvent('cartchange', { detail: { brand } }));
  };

  // keep tabs / pages in sync
  useEffect(() => {
    if (ephemeral) return;
    const onStorage = (e) => {
      if (e.key === cartKey(brand)) {
        try { setItems(JSON.parse(e.newValue) || {}); } catch { setItems({}); }
      }
    };
    window.addEventListener('storage', onStorage);
    return () => window.removeEventListener('storage', onStorage);
  }, [brand, ephemeral]);

  const persist = useCallback((next) => {
    setItems(next);
    write(next);
  }, [brand, ephemeral]);

  const setQty = useCallback((id, qty) => {
    setItems((prev) => {
      const next = { ...prev };
      if (qty <= 0) delete next[id]; else next[id] = qty;
      write(next);
      return next;
    });
  }, [brand, ephemeral]);

  const add = useCallback((id, n = 1) => {
    setItems((prev) => {
      const next = { ...prev, [id]: (prev[id] || 0) + n };
      write(next);
      return next;
    });
  }, [brand, ephemeral]);

  const clear = useCallback(() => persist({}), [persist]);

  // cross-component live updates within the same page
  useEffect(() => {
    if (ephemeral) return;
    const onCart = (e) => {
      if (e.detail?.brand !== brand) return;
      try { setItems(JSON.parse(localStorage.getItem(cartKey(brand))) || {}); }
      catch { setItems({}); }
    };
    window.addEventListener('cartchange', onCart);
    return () => window.removeEventListener('cartchange', onCart);
  }, [brand, ephemeral]);

  return { items, add, setQty, clear };
}

function cartCount(items) {
  return Object.values(items).reduce((a, b) => a + b, 0);
}
function cartTotal(items, products) {
  return products.reduce((sum, p) => sum + (items[p.id] || 0) * p.price, 0);
}
function money(cur, n) {
  return cur + (Number.isInteger(n) ? n : n.toFixed(2));
}

/* ---- Icons (simple line glyphs) ----------------------------- */
const Icon = {
  cart: (p) => (<svg viewBox="0 0 24 24" fill="none" width="20" height="20" {...p}><path d="M3 4h2l2.4 11.2a1 1 0 0 0 1 .8h8.2a1 1 0 0 0 1-.8L20 8H6" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/><circle cx="9.5" cy="20" r="1.4" fill="currentColor"/><circle cx="17.5" cy="20" r="1.4" fill="currentColor"/></svg>),
  plus: (p) => (<svg viewBox="0 0 24 24" fill="none" width="18" height="18" {...p}><path d="M12 5v14M5 12h14" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>),
  minus: (p) => (<svg viewBox="0 0 24 24" fill="none" width="18" height="18" {...p}><path d="M5 12h14" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>),
  arrow: (p) => (<svg viewBox="0 0 24 24" fill="none" width="18" height="18" {...p}><path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>),
  pin: (p) => (<svg viewBox="0 0 24 24" fill="none" width="20" height="20" {...p}><path d="M12 21s7-5.6 7-11a7 7 0 1 0-14 0c0 5.4 7 11 7 11Z" stroke="currentColor" strokeWidth="1.7"/><circle cx="12" cy="10" r="2.4" stroke="currentColor" strokeWidth="1.7"/></svg>),
  clock: (p) => (<svg viewBox="0 0 24 24" fill="none" width="20" height="20" {...p}><circle cx="12" cy="12" r="8.2" stroke="currentColor" strokeWidth="1.7"/><path d="M12 7.5V12l3 2" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/></svg>),
  phone: (p) => (<svg viewBox="0 0 24 24" fill="none" width="20" height="20" {...p}><path d="M6 3.5h3l1.5 4-2 1.4a11 11 0 0 0 5.1 5.1l1.4-2 4 1.5V20a1.5 1.5 0 0 1-1.6 1.5C11.6 21 3 12.4 3 5.1A1.5 1.5 0 0 1 4.5 3.5H6Z" stroke="currentColor" strokeWidth="1.7" strokeLinejoin="round"/></svg>),
  mail: (p) => (<svg viewBox="0 0 24 24" fill="none" width="20" height="20" {...p}><rect x="3.5" y="5.5" width="17" height="13" rx="2.2" stroke="currentColor" strokeWidth="1.7"/><path d="m4.5 7 7.5 5.2L19.5 7" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/></svg>),
  ig: (p) => (<svg viewBox="0 0 24 24" fill="none" width="20" height="20" {...p}><rect x="4" y="4" width="16" height="16" rx="5" stroke="currentColor" strokeWidth="1.7"/><circle cx="12" cy="12" r="3.6" stroke="currentColor" strokeWidth="1.7"/><circle cx="17" cy="7" r="1.1" fill="currentColor"/></svg>),
  check: (p) => (<svg viewBox="0 0 24 24" fill="none" width="22" height="22" {...p}><path d="M5 12.5 10 17.5 19 7" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>),
  star: (p) => (<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16" {...p}><path d="m12 3 2.6 5.6 6 .7-4.5 4.1 1.2 6L12 16.9 6.7 19.4l1.2-6L3.4 9.3l6-.7L12 3Z"/></svg>),
};

/* ---- Image placeholder ------------------------------------- */
const IMAGE_ASSETS = {
  "hero — pizza out of the oven, charred crust": "shared/assets/pizza/hero-pizza-transparent.png",
  "about — dough being stretched by hand": "shared/assets/pizza/about-dough-stretching.mp4",
  "margherita pizza": "shared/assets/pizza/menu/margherita.jpg",
  "marinara pizza": "shared/assets/pizza/menu/marinara.jpg",
  "diavola pizza": "shared/assets/pizza/menu/diavola.jpg",
  "napoletana pizza": "shared/assets/pizza/menu/napoletana.jpg",
  "sausage pizza": "shared/assets/pizza/menu/salsiccia.jpg",
  "capricciosa pizza": "shared/assets/pizza/menu/capricciosa.jpg",
  "bufalina pizza": "shared/assets/pizza/menu/bufalina.jpg",
  "four cheese pizza": "shared/assets/pizza/menu/quattro.jpg",
  "mushroom pizza": "shared/assets/pizza/menu/funghi.jpg",
  "prosciutto pizza": "shared/assets/pizza/menu/prosciutto.jpg",
  "potato pizza": "shared/assets/pizza/menu/patate.jpg",
  "vegetable pizza": "shared/assets/pizza/menu/vegetariana.jpg",
  "truffle pizza": "shared/assets/pizza/menu/tartufo.jpg",
  "nduja pizza": "shared/assets/pizza/menu/nduja.jpg",
  "burrata pizza": "shared/assets/pizza/menu/burrata.jpg",
  "calzone": "shared/assets/pizza/menu/calzone.jpg",
  "hero — tray of glazed doughnuts, glossy": "shared/assets/doughnut/header-doughnut-cutout.png",
  "about — baker glazing doughnuts by hand": "shared/assets/doughnut/about-glazing.png",
  "glazed doughnut": "shared/assets/doughnut/menu/glazed.jpg",
  "cinnamon sugar doughnut": "shared/assets/doughnut/menu/cinnamon.jpg",
  "chocolate old fashioned": "shared/assets/doughnut/menu/oldfashion.jpg",
  "vanilla bean doughnut": "shared/assets/doughnut/menu/vanilla.jpg",
  "strawberry sprinkle doughnut": "shared/assets/doughnut/menu/sprinkle.jpg",
  "double chocolate doughnut": "shared/assets/doughnut/menu/cocoa.jpg",
  "boston cream doughnut": "shared/assets/doughnut/menu/boston.jpg",
  "raspberry jam doughnut": "shared/assets/doughnut/menu/raspberry.jpg",
  "lemon curd doughnut": "shared/assets/doughnut/menu/lemon.jpg",
  "salted caramel doughnut": "shared/assets/doughnut/menu/caramel.jpg",
  "bavarian cream doughnut": "shared/assets/doughnut/menu/bavarian.jpg",
  "hazelnut chocolate doughnut": "shared/assets/doughnut/menu/nutella.jpg",
  "maple bacon doughnut": "shared/assets/doughnut/menu/maple.jpg",
  "matcha doughnut": "shared/assets/doughnut/menu/matcha.jpg",
  "pistachio doughnut": "shared/assets/doughnut/menu/pistachio.jpg",
  "birthday cake doughnut": "shared/assets/doughnut/menu/birthday.jpg",
};

function Ph({ label, className = "", center = false, style }) {
  const directSrc = typeof label === "string" && (/[/\\]/.test(label) || /\.(png|jpe?g|webp|gif|svg|mp4|webm|ogg)$/i.test(label));
  const assetSrc = directSrc ? label : IMAGE_ASSETS[label];
  const src = resolveAssetSrc(assetSrc);
  const isVideo = /\.(mp4|webm|ogg)$/i.test(src || "");
  const assetClass = src?.includes("/pizza/") ? "pizza-img" : "";
  const imageStyle = src && !isVideo
    ? { ...style, backgroundImage: `url("${src}")` }
    : style;
  const artStyle = src && !isVideo ? { backgroundImage: `url("${src}")` } : null;

  return (
    <div className={`ph ${src ? "has-img" : ""} ${isVideo ? "has-video" : ""} ${assetClass} ${center ? "center" : ""} ${className}`}
         data-label={label} style={imageStyle} role="img" aria-label={label}>
      {src && !isVideo && <span className="ph-art" style={artStyle} aria-hidden="true" />}
      {isVideo && <VisibleVideo src={src} />}
    </div>
  );
}

function VisibleVideo({ src }) {
  const ref = React.useRef(null);

  React.useEffect(() => {
    const video = ref.current;
    if (!video) return;

    const update = () => {
      const r = video.getBoundingClientRect();
      const fullyVisible = r.top >= 0 && r.left >= 0 && r.bottom <= window.innerHeight && r.right <= window.innerWidth;
      if (fullyVisible) video.play().catch(() => {});
      else video.pause();
    };

    update();
    const observer = new IntersectionObserver(update, { threshold: [0, 1] });
    observer.observe(video);
    window.addEventListener("resize", update);
    return () => {
      observer.disconnect();
      window.removeEventListener("resize", update);
    };
  }, []);

  return (
    <video ref={ref} className="ph-video" src={src} preload="auto" muted loop playsInline aria-hidden="true" />
  );
}

function resolveAssetSrc(src) {
  if (!src || /^https?:\/\//i.test(src) || src.startsWith("/") || src.startsWith("shared/")) return src;
  return `${window.PROJECT_BASE_URL || ""}${src.replace(/^\.\//, "")}`;
}

/* ---- Quantity stepper -------------------------------------- */
function Stepper({ qty, onAdd, onSub, size = "md" }) {
  const sm = size === "sm";
  if (!qty) {
    return (
      <button className={`add-btn ${sm ? "sm" : ""}`} onClick={onAdd} aria-label="Add to order">
        <Icon.plus /> <span>Add</span>
      </button>
    );
  }
  return (
    <div className={`stepper ${sm ? "sm" : ""}`}>
      <button onClick={onSub} aria-label="Remove one"><Icon.minus /></button>
      <span className="tabnum">{qty}</span>
      <button onClick={onAdd} aria-label="Add one"><Icon.plus /></button>
    </div>
  );
}

/* ---- Product card (3 styles via `variant`) ------------------ */
function ProductCard({ p, cur, qty, onAdd, onSub, variant }) {
  const tag = p.tag && (
    <span className="tag">{p.tag}</span>
  );

  if (variant === "list") {
    return (
      <div className={`pc-list ${qty ? "active" : ""}`}>
        <Ph label={p.img} className="pc-list-img" center />
        <div className="pc-list-body">
          <div className="pc-list-head">
            <h3>{p.name} {tag}</h3>
            <span className="pc-price tabnum">{money(cur, p.price)}</span>
          </div>
          <p className="muted pc-desc">{p.desc}</p>
        </div>
        <div className="pc-list-action">
          <Stepper qty={qty} onAdd={onAdd} onSub={onSub} size="sm" />
        </div>
      </div>
    );
  }

  if (variant === "editorial") {
    return (
      <div className={`pc-ed ${qty ? "active" : ""}`}>
        <div className="pc-ed-imgwrap">
          <Ph label={p.img} className="pc-ed-img" />
          {tag}
          <span className="pc-ed-price tabnum">{money(cur, p.price)}</span>
        </div>
        <div className="pc-ed-foot">
          <div>
            <h3>{p.name}</h3>
            <p className="muted pc-desc">{p.desc}</p>
          </div>
          <Stepper qty={qty} onAdd={onAdd} onSub={onSub} />
        </div>
      </div>
    );
  }

  // default: "clean"
  return (
    <div className={`pc ${qty ? "active" : ""}`}>
      <div className="pc-imgwrap">
        <Ph label={p.img} className="pc-img" />
        {tag}
      </div>
      <div className="pc-body">
        <div className="pc-head">
          <h3>{p.name}</h3>
          <span className="pc-price tabnum">{money(cur, p.price)}</span>
        </div>
        <p className="muted pc-desc">{p.desc}</p>
        <div className="pc-foot">
          <Stepper qty={qty} onAdd={onAdd} onSub={onSub} />
        </div>
      </div>
    </div>
  );
}

/* ---- Top navigation ---------------------------------------- */
const NAV = [
  { id: "home", label: "Home" },
  { id: "menu", label: "Menu" },
  { id: "order", label: "Order" },
  { id: "contact", label: "Contact" },
];

/* ---- Open-now status (live, computed in the venue's timezone) - */
const DOW = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const toMin = (hhmm) => { const [h, m] = hhmm.split(":").map(Number); return h * 60 + m; };
const to12 = (hhmm) => {
  let [h, m] = hhmm.split(":").map(Number);
  const ap = h >= 12 ? "pm" : "am";
  h = h % 12 || 12;
  return m ? `${h}:${String(m).padStart(2, "0")} ${ap}` : `${h} ${ap}`;
};
// "Now" resolved in the business timezone, so the badge is correct for any visitor.
function zonedNow(tz) {
  try {
    const parts = new Intl.DateTimeFormat("en-US", {
      timeZone: tz, weekday: "short", hour: "2-digit", minute: "2-digit", hour12: false,
    }).formatToParts(new Date());
    const p = Object.fromEntries(parts.map((x) => [x.type, x.value]));
    let hour = parseInt(p.hour, 10); if (hour === 24) hour = 0;
    return { day: DOW.indexOf(p.weekday), min: hour * 60 + parseInt(p.minute, 10) };
  } catch {
    const d = new Date();
    return { day: d.getDay(), min: d.getHours() * 60 + d.getMinutes() };
  }
}

function OpenStatus({ info }) {
  const sched = info?.openHours;
  const tz = info?.tz || "America/New_York";
  const [now, setNow] = useState(() => zonedNow(tz));
  useEffect(() => {
    const id = setInterval(() => setNow(zonedNow(tz)), 60000);
    return () => clearInterval(id);
  }, [tz]);
  if (!Array.isArray(sched) || sched.length !== 7) return null;

  const today = sched[now.day];
  const open = !!(today && now.min >= toMin(today.open) && now.min < toMin(today.close));

  let detail;
  if (open) {
    detail = `till ${to12(today.close)}`;
  } else if (today && now.min < toMin(today.open)) {
    detail = `opens ${to12(today.open)}`;
  } else {
    for (let i = 1; i <= 7; i++) {
      const d = sched[(now.day + i) % 7];
      if (d) { detail = `opens ${DOW[(now.day + i) % 7]} ${to12(d.open)}`; break; }
    }
  }

  return (
    <span className={`open-status ${open ? "is-open" : "is-closed"}`}
          title={`${open ? "Open now" : "Closed"}${detail ? ` — ${detail}` : ""}`}>
      <span className="open-dot" aria-hidden="true" />
      <span className="open-label">{open ? "Open now" : "Closed"}</span>
      {detail && <span className="open-detail">· {detail}</span>}
    </span>
  );
}

function Nav({ brand, route, go, count }) {
  const [scrolled, setScrolled] = useState(false);
  const [open, setOpen] = useState(false);
  const logo = brand.logo || brand.name[0];
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  useEffect(() => { setOpen(false); }, [route]);

  return (
    <header className={`nav ${scrolled ? "scrolled" : ""}`}>
      <div className="wrap nav-inner">
        <button className="brand" onClick={() => go("home")}>
          <span className="brand-mark" aria-hidden="true">{logo}</span>
          <span className="brand-text">
            <span className="brand-name">{brand.name}</span>
            {brand.headerNote && <span className="brand-sub">{brand.headerNote}</span>}
          </span>
        </button>

        <nav className="nav-links">
          {NAV.map((n) => (
            <button key={n.id} className={route === n.id ? "active" : ""}
                    onClick={() => go(n.id)}>{n.label}</button>
          ))}
        </nav>

        <div className="nav-right">
          <OpenStatus info={brand.info} />
          <button className="cart-pill" onClick={() => go("order")} aria-label={`Order — ${count} items`}>
            <Icon.cart />
            {count > 0 && <span className="cart-count tabnum">{count}</span>}
          </button>
          <button className="nav-burger" onClick={() => setOpen((o) => !o)} aria-label="Menu">
            <span /><span /><span />
          </button>
        </div>
      </div>

      {open && (
        <div className="nav-drawer">
          {NAV.map((n) => (
            <button key={n.id} className={route === n.id ? "active" : ""}
                    onClick={() => go(n.id)}>{n.label}</button>
          ))}
        </div>
      )}
    </header>
  );
}

/* ---- Footer ------------------------------------------------- */
function Footer({ brand, go }) {
  const { info } = brand;
  const handle = brand.social?.instagram || brand.socialHandle || brand.id;
  const footerNote = brand.footerNote || "Made with care";
  const logo = brand.logo || brand.name[0];
  return (
    <footer className="ft">
      <div className="wrap ft-inner">
        <div className="ft-brand">
          <div className="brand">
            <span className="brand-mark" aria-hidden="true">{logo}</span>
            <span className="brand-name">{brand.name}</span>
          </div>
          <p className="muted">{brand.tagline}</p>
          <a className="ft-ig" href="#" onClick={(e) => e.preventDefault()}>
            <Icon.ig /> @{handle}
          </a>
        </div>

        <div className="ft-col">
          <h4>Visit</h4>
          {info.address.map((l, i) => <p key={i} className="muted">{l}</p>)}
          <p className="muted">{info.phone}</p>
        </div>

        <div className="ft-col">
          <h4>Hours</h4>
          {info.hours.map((h, i) => (
            <p key={i} className="muted ft-hours"><span>{h.d}</span><span>{h.h}</span></p>
          ))}
        </div>

        <div className="ft-col">
          <h4>Pages</h4>
          {NAV.map((n) => (
            <button key={n.id} className="ft-link" onClick={() => go(n.id)}>{n.label}</button>
          ))}
        </div>
      </div>
      <div className="wrap ft-base">
        <span className="muted mono">© {new Date().getFullYear()} {brand.name}. A demo storefront.</span>
        <span className="muted mono">{footerNote}</span>
      </div>
    </footer>
  );
}

Object.assign(window, {
  useCart, cartCount, cartTotal, money, Icon, Ph, Stepper, ProductCard, Nav, Footer, NAV,
});
