// Shared UI components and helpers for Hazelbelle site.

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

// ---------- Helpers ----------
function uid() {
  return Math.random().toString(36).slice(2, 8).toUpperCase();
}

function makeOrderRef() {
  // HB-YYMMDD-XXXX
  const d = new Date();
  const yy = String(d.getFullYear()).slice(2);
  const mm = String(d.getMonth() + 1).padStart(2, "0");
  const dd = String(d.getDate()).padStart(2, "0");
  return `HB-${yy}${mm}${dd}-${uid().slice(0, 4)}`;
}

function fmtDate(ts) {
  const d = new Date(ts);
  return d.toLocaleDateString(undefined, { day: "numeric", month: "short", year: "numeric" });
}
function fmtTime(ts) {
  return new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
}
function fmtPrice(p) {
  if (p == null || p === "") return "—";
  const n = Number(p);
  if (Number.isNaN(n)) return p;
  return `$${n.toFixed(2)}`;
}

function classNames(...xs) { return xs.filter(Boolean).join(" "); }

// [HB] Hidden spam-trap field for public forms. Real people never see or fill it
// — it's positioned off-screen, skipped by keyboard tabbing and labelled for the
// rare screen-reader that reaches it. Spam bots auto-fill every field they find,
// so the server (lib/form-honeypot.js) drops any submission where this comes back
// non-empty. The field name "website" must match HONEYPOT_FIELD on the server.
function Honeypot({ value, onChange }) {
  return (
    <div aria-hidden="true" style={{ position: "absolute", left: "-9999px", top: "auto", width: 1, height: 1, overflow: "hidden" }}>
      <label>Leave this field empty
        <input
          type="text"
          name="website"
          tabIndex={-1}
          autoComplete="off"
          value={value || ""}
          onChange={e => onChange(e.target.value)}
        />
      </label>
    </div>
  );
}

// ---------- App Context ----------
const AppCtx = createContext(null);
const useApp = () => useContext(AppCtx);

// ---------- Persistent state ----------
function useLocalState(key, initial) {
  const [val, setVal] = useState(() => {
    try {
      const raw = localStorage.getItem(key);
      if (raw == null) return typeof initial === "function" ? initial() : initial;
      return JSON.parse(raw);
    } catch (e) {
      return typeof initial === "function" ? initial() : initial;
    }
  });
  useEffect(() => {
    try { localStorage.setItem(key, JSON.stringify(val)); } catch (e) {}
  }, [key, val]);
  return [val, setVal];
}

// ---------- Brand mark / nav ----------
function BrandMark({ size = 44 }) {
  return (
    <a className="nav-brand" href="#/" aria-label="Hazelbelle home">
      <img src="assets/hazelbelle-logo.jpg" alt="Hazelbelle logo" style={{ width: size, height: size }} />
      <span className="nav-brand-text">
        <span className="script">Hazelbelle</span>
        <span className="smallcaps">Embroidery &amp; Print</span>
      </span>
    </a>
  );
}

function NavBar({ route, go }) {
  const app = useApp();
  const [open, setOpen] = useState(false);
  const path = (route || "").split("?")[0];
  const customer = app && app.customer;
  const cartCount = (app && app.cart ? app.cart : []).reduce((n, x) => n + (x.qty || 0), 0); // [HB-ADD]
  const links = [
    { to: "/", label: "Home" },
    { to: "/shop", label: "Shop & Examples" },
    { to: "/blanks", label: "Shop blanks" },
    { to: "/quote", label: "Get a quote" },
    { to: "/blog", label: "Journal" },
    { to: "/upload", label: "Upload a design" },
    { to: "/about", label: "About" },
    { to: "/contact", label: "Contact" },
  ];
  const close = () => setOpen(false);
  useEffect(() => { setOpen(false); }, [route]);
  return (
    <nav className="nav">
      <div className="nav-inner">
        <BrandMark />
        <div className={classNames("nav-links", open && "open")}>
          {links.map(l => (
            <a
              key={l.to}
              className={classNames("nav-link", (l.to === "/" ? path === "/" : path.startsWith(l.to)) && "active")}
              href={`#${l.to}`}
              onClick={close}
            >{l.label}</a>
          ))}
          <a
            className={classNames("nav-link", "nav-link-account", path.startsWith("/account") && "active")}
            href="#/account"
            onClick={close}
          >{customer ? "My account" : "Sign in"}</a>
          {/* Labeled theme toggle for the mobile dropdown — the icon button in
              the bar is easy to miss on a phone. Hidden on desktop via CSS. */}
          <button
            type="button"
            className="nav-link nav-theme-row"
            onClick={() => { if (app && app.toggleTheme) app.toggleTheme(); close(); }}
          >{app && app.theme === "dark" ? "Switch to light mode" : "Switch to dark mode"}</button>
        </div>
        <div className="nav-actions">
          <button
            type="button"
            className="nav-theme-toggle"
            onClick={() => app.toggleTheme && app.toggleTheme()}
            aria-label={app && app.theme === "dark" ? "Switch to light mode" : "Switch to dark mode"}
            title={app && app.theme === "dark" ? "Light mode" : "Dark mode"}
          >
            {app && app.theme === "dark" ? (
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <circle cx="12" cy="12" r="4.2" />
                <path d="M12 2.5v2M12 19.5v2M4.6 4.6l1.4 1.4M18 18l1.4 1.4M2.5 12h2M19.5 12h2M4.6 19.4 6 18M18 6l1.4-1.4" />
              </svg>
            ) : (
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <path d="M20 14.5A8 8 0 0 1 9.5 4 6.5 6.5 0 1 0 20 14.5z" />
              </svg>
            )}
          </button>
          <a
            className={classNames("nav-link", "nav-link-cart", path.startsWith("/cart") && "active")}
            href="#/cart"
            onClick={close}
            aria-label={cartCount > 0 ? `Cart, ${cartCount} item${cartCount === 1 ? "" : "s"}` : "Cart"}
            title="Cart"
            style={{ position: "relative", display: "inline-flex", alignItems: "center", padding: "8px 12px" }}
          >
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <circle cx="9" cy="21" r="1.5" />
              <circle cx="20" cy="21" r="1.5" />
              <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" />
            </svg>
            {cartCount > 0 && (
              <span aria-hidden="true" style={{ position: "absolute", top: 2, right: 4, minWidth: 16, height: 16, padding: "0 4px", borderRadius: 999, background: "var(--rose-deep)", color: "#fff", fontSize: 11, lineHeight: "16px", textAlign: "center", fontFamily: "'Courier New', monospace", fontWeight: 600 }}>{cartCount}</span>
            )}
          </a>
          <button
            type="button"
            className="nav-toggle"
            aria-label={open ? "Close menu" : "Open menu"}
            aria-expanded={open}
            onClick={() => setOpen(o => !o)}
          >
            {open ? (
              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18" /></svg>
            ) : (
              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><path d="M3 6h18M3 12h18M3 18h18" /></svg>
            )}
          </button>
        </div>
      </div>
    </nav>
  );
}


// ---------- Thread divider (wavy embroidery line) ----------
function ThreadDivider({ color }) {
  return (
    <svg className="thread" viewBox="0 0 1200 16" preserveAspectRatio="none" aria-hidden="true">
      <path
        d="M0 8 Q 30 0, 60 8 T 120 8 T 180 8 T 240 8 T 300 8 T 360 8 T 420 8 T 480 8 T 540 8 T 600 8 T 660 8 T 720 8 T 780 8 T 840 8 T 900 8 T 960 8 T 1020 8 T 1080 8 T 1140 8 T 1200 8"
        fill="none"
        stroke={color || "currentColor"}
        strokeWidth="1.5"
        strokeDasharray="4 3"
        strokeLinecap="round"
      />
    </svg>
  );
}

// ---------- Footer ----------
function Footer() {
  const app = useApp();
  return (
    <footer className="footer">
      <div className="footer-inner">
        <div>
          <BrandMark />
          <p style={{ marginTop: 14, color: "var(--ink-2)", maxWidth: 36 + "ch" }}>
            Embroidery &amp; bespoke print, made with care in {app.tweaks.location || "your local studio"}.
          </p>
        </div>
        <div>
          <h4>Shop</h4>
          <div className="footer-links">
            <a href="#/shop">Shop &amp; examples</a>
            <a href="#/upload">Upload a design</a>
          </div>
        </div>
        <div>
          <h4>Studio</h4>
          <div className="footer-links">
            <a href="#/about">About Joy</a>
            <a href="#/contact">Contact</a>
            <a href="#/blog">Journal</a>
            <a href="#/faq">FAQ</a>
          </div>
        </div>
        <div>
          <h4>Policies</h4>
          <div className="footer-links">
            <a href="#/legal/terms">Terms of Service</a>
            <a href="#/legal/privacy">Privacy Policy</a>
            <a href="#/legal/refunds">Returns &amp; Refunds</a>
            <a href="#/legal/liability">Liability Disclaimer</a>
          </div>
        </div>
        <div>
          <h4>Follow along</h4>
          <div className="footer-links">
            <a href={app.tweaks.facebookUrl || "https://www.facebook.com/profile.php?id=61590039444991"} target="_blank" rel="noopener">Facebook</a>
            <a href="#/admin" style={{ color: "var(--ink-3)", fontSize: 13 }}>Studio login</a>
          </div>
        </div>
      </div>
      <div className="footer-bottom">
        <span>© {new Date().getFullYear()} Hazelbelle Embroidery &amp; Print</span>
        <span className="mono">made with love · one stitch at a time</span>
      </div>
    </footer>
  );
}

// ---------- Toast ----------
function Toast({ msg, onDone }) {
  useEffect(() => {
    if (!msg) return;
    const t = setTimeout(onDone, 3200);
    return () => clearTimeout(t);
  }, [msg, onDone]);
  if (!msg) return null;
  return <div className="toast">{msg}</div>;
}

// ---------- File chip ----------
function FileChip({ file, onRemove }) {
  const isImg = file.dataUrl && file.dataUrl.startsWith("data:image");
  return (
    <div className="file-chip">
      <div className="file-chip-thumb">
        {isImg
          ? <img src={file.dataUrl} alt={file.name} />
          : <div className="ph-stripes" style={{ fontSize: 9 }}>{(file.name || "").split(".").pop().toUpperCase()}</div>}
      </div>
      <div style={{ minWidth: 0 }}>
        <div style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: 180 }}>{file.name}</div>
        <div style={{ color: "var(--ink-3)", fontSize: 12 }}>{Math.round((file.size || 0) / 1024)} KB</div>
      </div>
      {onRemove && <button className="file-chip-x" onClick={onRemove} aria-label="Remove">×</button>}
    </div>
  );
}

// ---------- Modal ----------
function Modal({ open, onClose, children }) {
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <button className="modal-close" onClick={onClose} aria-label="Close">×</button>
        {children}
      </div>
    </div>
  );
}

// ---------- Placeholder image ----------
function Placeholder({ label, variant }) {
  return <div className={classNames("ph-stripes", variant === "sage" && "ph-stripes-sage")}>{label}</div>;
}

// Read file as data URL
function readFileAsDataURL(file) {
  return new Promise((resolve, reject) => {
    const r = new FileReader();
    r.onload = () => resolve(r.result);
    r.onerror = reject;
    r.readAsDataURL(file);
  });
}

// Make a small JPEG data-URL thumbnail from an image file (for inline previews
// in the admin, stored alongside the upload). Resolves "" if not an image or on
// any failure. SVGs are skipped (canvas export can fail/taint).
function makeThumbnail(file, maxDim = 480) {
  return new Promise((resolve) => {
    if (!file || !file.type || !file.type.startsWith("image/") || file.type === "image/svg+xml") {
      return resolve("");
    }
    const url = URL.createObjectURL(file);
    const img = new Image();
    img.onload = () => {
      try {
        const scale = Math.min(1, maxDim / Math.max(img.width, img.height));
        const w = Math.max(1, Math.round(img.width * scale));
        const h = Math.max(1, Math.round(img.height * scale));
        const canvas = document.createElement("canvas");
        canvas.width = w; canvas.height = h;
        canvas.getContext("2d").drawImage(img, 0, 0, w, h);
        resolve(canvas.toDataURL("image/jpeg", 0.72));
      } catch (e) { resolve(""); }
      finally { URL.revokeObjectURL(url); }
    };
    img.onerror = () => { URL.revokeObjectURL(url); resolve(""); };
    img.src = url;
  });
}

Object.assign(window, {
  useState, useEffect, useRef, useCallback, useMemo, createContext, useContext,
  AppCtx, useApp,
  uid, makeOrderRef, fmtDate, fmtTime, fmtPrice, classNames,
  useLocalState,
  BrandMark, NavBar, ThreadDivider, Footer, Toast, FileChip, Modal, Placeholder,
  readFileAsDataURL, makeThumbnail,
});
