// src/ui.jsx — shared primitives: Btn, Pill, AddToCart stepper variants, Placeholder, SectionHeader, Rating, FeeRow

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

// Placeholder image tile. Pass `img="keyword,keyword"` to render an Unsplash Source photo with a
// graceful fallback to the striped placeholder if it fails to load (or network's blocked).
// `seed` locks a specific image for a given product so it doesn't swap on re-render.
function Ph({ label, aspect = "1 / 1", radius, style, img, seed, children }) {
  const [loaded, setLoaded] = React.useState(false);
  const [errored, setErrored] = React.useState(false);
  // img can be: undefined | string keyword | { url } | { keyword }
  let src = null;
  if (img && !errored) {
    if (typeof img === "string") {
      src = `https://loremflickr.com/600/600/${encodeURIComponent(img)}/all?lock=${encodeURIComponent(seed || img)}`;
    } else if (img.url) {
      src = img.url;
    } else if (img.keyword) {
      src = `https://loremflickr.com/600/600/${encodeURIComponent(img.keyword)}/all?lock=${encodeURIComponent(seed || img.keyword)}`;
    }
  }
  return (
    <div className="ph" style={{ aspectRatio: aspect, borderRadius: radius, ...style }}>
      {src && (
        <img
          className={`ph-img ${loaded ? "loaded" : "loading"}`}
          src={src}
          alt={label || ""}
          onLoad={() => setLoaded(true)}
          onError={() => setErrored(true)}
        />
      )}
      {(!src || !loaded) && <span className="ph-lbl">{label}</span>}
      {src && loaded && <div className="ph-scrim" aria-hidden />}
      {children}
    </div>
  );
}

// Pill — small capsule for metadata (eta, fee, tags)
function Pill({ children, tone = "default", icon, style }) {
  const tones = {
    default: { bg: "var(--surface-2)", color: "var(--ink-2)", border: "var(--line)" },
    navy:    { bg: "var(--navy-tint)", color: "var(--navy)", border: "transparent" },
    accent:  { bg: "var(--accent-tint)", color: "var(--accent)", border: "transparent" },
    ghost:   { bg: "transparent", color: "var(--ink-3)", border: "var(--line)" },
    dark:    { bg: "var(--navy-fill)", color: "#fff", border: "transparent" },
    ok:      { bg: "var(--ok-tint)", color: "var(--ok)", border: "transparent" },
  }[tone];
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 6,
      padding: "4px 10px", borderRadius: 999,
      background: tones.bg, color: tones.color, border: `1px solid ${tones.border}`,
      fontSize: 12, fontWeight: 500, whiteSpace: "nowrap",
      ...style,
    }}>
      {icon && <span style={{ display: "inline-flex" }}>{icon}</span>}
      {children}
    </span>
  );
}

// Btn — primary / secondary / ghost
function Btn({ children, variant = "primary", size = "md", full, icon, iconRight, onClick, type = "button", style, disabled, ...rest }) {
  const sizes = {
    sm: { h: 32, px: 14, fs: 13 },
    md: { h: 44, px: 18, fs: 14 },
    lg: { h: 52, px: 22, fs: 15 },
  }[size];
  const variants = {
    primary:   { bg: "var(--navy-fill)", color: "#fff", border: "transparent" },
    secondary: { bg: "var(--surface)", color: "var(--ink)", border: "var(--line)" },
    ghost:     { bg: "transparent", color: "var(--ink-2)", border: "transparent" },
    accent:    { bg: "var(--accent)", color: "#fff", border: "transparent" },
    inverse:   { bg: "#fff", color: "var(--navy)", border: "transparent" },
  }[variant];
  return (
    <button type={type} onClick={onClick} disabled={disabled}
      className="btn-reset"
      style={{
        display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 8,
        height: sizes.h, padding: `0 ${sizes.px}px`,
        background: variants.bg, color: variants.color,
        border: `1px solid ${variants.border}`,
        borderRadius: 999,
        fontSize: sizes.fs, fontWeight: 600, letterSpacing: "-0.005em",
        width: full ? "100%" : "auto",
        cursor: disabled ? "not-allowed" : "pointer",
        opacity: disabled ? 0.5 : 1,
        transition: "transform 0.08s ease, background 0.15s ease, box-shadow 0.15s ease",
        ...style,
      }}
      onMouseDown={(e) => { if (!disabled) e.currentTarget.style.transform = "scale(0.97)"; }}
      onMouseUp={(e)   => { e.currentTarget.style.transform = "scale(1)"; }}
      onMouseLeave={(e) => { e.currentTarget.style.transform = "scale(1)"; }}
      {...rest}>
      {icon}
      {children}
      {iconRight}
    </button>
  );
}

// SectionHeader — used throughout
function SectionHeader({ eyebrow, title, action, children, style }) {
  return (
    <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 16, marginBottom: 16, ...style }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        {eyebrow && <div className="uppercase-mono" style={{ marginBottom: 6 }}>{eyebrow}</div>}
        {title && <h2 className="serif" style={{ fontSize: 28, lineHeight: 1.2, color: "var(--ink)", margin: 0, paddingBottom: 4, textWrap: "balance" }}>{title}</h2>}
        {children}
      </div>
      {action}
    </div>
  );
}

// Rating — star + number
function Rating({ value, reviews, size = 13 }) {
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: size, color: "var(--ink-2)" }}>
      <Icons.Star size={size} stroke={0} style={{ fill: "var(--navy)", stroke: "none" }} />
      <span className="num" style={{ fontWeight: 600, color: "var(--ink)" }}>{value}</span>
      {reviews != null && <span style={{ color: "var(--ink-4)" }}>({reviews.toLocaleString()})</span>}
    </span>
  );
}

// Add-to-cart — three behaviours exposed via tweaks:
//  • 'morph'  : single Add button morphs into stepper once qty > 0
//  • 'stepper': always a stepper (– 0 +) — most Instacart-like
//  • 'quick'  : primary Add, quantity via long-press / right-click menu
function AddStepper({ qty, onAdd, onRemove, size = "md", pattern = "morph", compact, tone = "navy" }) {
  const h = size === "sm" ? 32 : size === "lg" ? 48 : 38;
  const fs = size === "sm" ? 13 : 14;

  const toneMap = {
    navy:    { bg: "var(--navy-fill)",   color: "#fff" },
    outline: { bg: "var(--surface)", color: "var(--ink)" },
  }[tone];

  if (pattern === "stepper" || qty > 0) {
    if (qty === 0 && pattern === "stepper") {
      // Always-stepper: show "Add" that immediately becomes –0+. Simpler: show +.
      return (
        <button className="btn-reset" onClick={onAdd}
          aria-label="Add to cart"
          style={{
            height: h, width: h, borderRadius: 999,
            background: toneMap.bg, color: toneMap.color,
            display: "inline-flex", alignItems: "center", justifyContent: "center",
            border: "1px solid " + (tone === "outline" ? "var(--line)" : "transparent"),
            boxShadow: "var(--shadow-sm)",
          }}>
          <Icons.Plus size={18} stroke={2.2} />
        </button>
      );
    }
    return (
      <div className="scale-in" style={{
        display: "inline-flex", alignItems: "center", height: h,
        background: toneMap.bg, color: toneMap.color,
        borderRadius: 999, padding: 2,
        boxShadow: "var(--shadow-sm)",
      }}>
        <button className="btn-reset" onClick={onRemove} aria-label="Decrement"
          style={{ height: h - 4, width: h - 4, borderRadius: 999, display: "inline-flex", alignItems: "center", justifyContent: "center", color: toneMap.color }}>
          {qty === 1 ? <Icons.Trash size={15} stroke={2} /> : <Icons.Minus size={16} stroke={2.4} />}
        </button>
        <span className="num" style={{ minWidth: 24, textAlign: "center", fontSize: fs, fontWeight: 600 }}>{qty}</span>
        <button className="btn-reset" onClick={onAdd} aria-label="Increment"
          style={{ height: h - 4, width: h - 4, borderRadius: 999, display: "inline-flex", alignItems: "center", justifyContent: "center", color: toneMap.color }}>
          <Icons.Plus size={16} stroke={2.4} />
        </button>
      </div>
    );
  }

  // morph / quick: single + or Add label
  return (
    <button className="btn-reset" onClick={onAdd}
      aria-label="Add to cart"
      style={{
        height: h, minWidth: h, padding: size === "lg" ? "0 18px" : 0,
        borderRadius: 999,
        background: tone === "outline" ? "var(--surface)" : "var(--navy-fill)",
        color: tone === "outline" ? "var(--ink)" : "#fff",
        border: "1px solid " + (tone === "outline" ? "var(--line)" : "transparent"),
        display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6,
        boxShadow: "var(--shadow-sm)",
        fontSize: fs, fontWeight: 600,
      }}>
      {size === "lg" ? <><Icons.Plus size={16} stroke={2.2} /> Add</> : <Icons.Plus size={18} stroke={2.2} />}
    </button>
  );
}

// Sticky cart-floater button
function CartFloater({ count, subtotal, onClick, visible }) {
  if (!visible || count === 0) return null;
  return (
    <div className="slide-up" style={{
      position: "fixed", left: "50%", transform: "translateX(-50%)",
      bottom: 24, zIndex: 50,
    }}>
      <button onClick={onClick}
        className="btn-reset"
        style={{
          display: "inline-flex", alignItems: "center", gap: 14,
          height: 56, padding: "0 8px 0 20px",
          background: "var(--navy-fill)", color: "#fff",
          borderRadius: 999, boxShadow: "var(--shadow-lg)",
          fontSize: 15, fontWeight: 600,
        }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 10 }}>
          <span style={{
            minWidth: 24, height: 24, padding: "0 8px",
            background: "rgba(255,255,255,.15)", borderRadius: 999,
            display: "inline-flex", alignItems: "center", justifyContent: "center",
            fontSize: 13, fontWeight: 600,
          }}>{count}</span>
          View cart
        </span>
        <span style={{ opacity: 0.7, fontSize: 13 }}>·</span>
        <span className="num">{money(subtotal)}</span>
        <span style={{
          height: 40, width: 40, background: "#fff", color: "var(--navy)",
          borderRadius: 999, display: "inline-flex", alignItems: "center", justifyContent: "center",
        }}>
          <Icons.Chevron size={18} stroke={2} />
        </span>
      </button>
    </div>
  );
}

// Ask-Opa floater — persistent shortcut to the AI hero from any screen.
// Bottom-left so it doesn't conflict with the centered CartFloater or the
// bottom-right tweaks FAB. Distinct shape: narrow pill with warm accent ring.
function AskOpaFloater({ visible, onClick }) {
  if (!visible) return null;
  return (
    <div className="slide-up" style={{
      position: "fixed", left: 20, bottom: 24, zIndex: 49,
    }}>
      <button onClick={onClick}
        className="btn-reset"
        style={{
          display: "inline-flex", alignItems: "center", gap: 8,
          height: 46, padding: "0 16px 0 14px",
          borderRadius: 999,
          background: "var(--navy-fill)",
          color: "#fff",
          fontSize: 13.5, fontWeight: 600,
          border: "1px solid oklch(from var(--accent) l c h / 0.5)",
          boxShadow: "0 10px 30px rgba(10,31,68,0.35), 0 0 0 1px oklch(from var(--accent) l c h / 0.18)",
        }}>
        <span style={{
          width: 26, height: 26, borderRadius: 999,
          background: "oklch(from var(--accent) l c h / 0.22)", color: "var(--accent)",
          display: "inline-flex", alignItems: "center", justifyContent: "center",
        }}>
          <Icons.Sparkle size={14} stroke={2.4} />
        </span>
        Ask Opa
      </button>
    </div>
  );
}

// Toast — tiny, top-center, auto-dismiss. Used for subtle confirmations.
function Toast({ message, visible }) {
  if (!visible) return null;
  return (
    <div style={{
      position: "fixed", top: 84, left: "50%", transform: "translateX(-50%)",
      background: "var(--navy-fill)", color: "#fff",
      padding: "10px 18px", borderRadius: 999,
      fontSize: 13, fontWeight: 500,
      boxShadow: "var(--shadow-lg)", zIndex: 60,
    }} className="slide-up">{message}</div>
  );
}

// BodegaMark — tiny "Ω" glyph + wordmark, navy. Doesn't touch real Opa brand beyond using the exclamation.
function OpaMark({ inverted, size = 24 }) {
  return (
    <span style={{ display: "inline-flex", alignItems: "baseline", gap: 2,
      fontFamily: '"Instrument Serif", Georgia, serif',
      fontSize: size, lineHeight: 1, color: inverted ? "#fff" : "var(--navy)",
      letterSpacing: "-0.01em", fontStyle: "italic",
    }}>
      <span style={{ fontWeight: 400 }}>Opa</span>
      <span style={{ color: "var(--accent)" }}>!</span>
    </span>
  );
}

Object.assign(window, { Ph, Pill, Btn, SectionHeader, Rating, AddStepper, CartFloater, AskOpaFloater, Toast, OpaMark });
