/* ============================================================
   lib.jsx — shared primitives, animation helpers, language + theme state.
   Loaded first on every page (text/babel, shared global scope).
   ============================================================ */
const { useState, useEffect, useRef, useCallback } = React;

function clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }
const DS_ASSET = "assets";

/* ---------- Language state (localStorage + custom event) ---------- */
function getLang() { return localStorage.getItem("mb_lang") || "pl"; }
function setLang(l) {
  localStorage.setItem("mb_lang", l);
  document.documentElement.lang = l;
  window.dispatchEvent(new Event("mb_langchange"));
}
function useLang() {
  const [lang, set] = useState(getLang);
  useEffect(() => {
    const h = () => set(getLang());
    window.addEventListener("mb_langchange", h);
    return () => window.removeEventListener("mb_langchange", h);
  }, []);
  return lang;
}
/* tx({pl,en}) -> string in current language; passes plain values through. */
function useTx() {
  const lang = useLang();
  return useCallback((o) => (o && typeof o === "object" && "pl" in o ? o[lang] : o), [lang]);
}

/* ---------- Theme state (.dark on <html>) ---------- */
function getTheme() { return localStorage.getItem("mb_theme") || "light"; }
function applyTheme(t) {
  document.documentElement.classList.toggle("dark", t === "dark");
  localStorage.setItem("mb_theme", t);
  window.dispatchEvent(new Event("mb_themechange"));
}
function useTheme() {
  const [theme, set] = useState(getTheme);
  useEffect(() => {
    const h = () => set(getTheme());
    window.addEventListener("mb_themechange", h);
    return () => window.removeEventListener("mb_themechange", h);
  }, []);
  return [theme, (t) => applyTheme(t)];
}

/* ---------- FadeIn (whileInView, once) ---------- */
function FadeIn({ children, as = "div", delay = 0, duration = 0.7, x = 0, y = 30, className = "", style = {} }) {
  const ref = useRef(null);
  const [show, setShow] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const check = () => {
      const r = el.getBoundingClientRect();
      if (r.top < window.innerHeight + 60 && r.bottom > -60) { setShow(true); return true; }
      return false;
    };
    if (check()) return;
    const onScroll = () => { if (check()) window.removeEventListener("scroll", onScroll); };
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); };
  }, []);
  const Tag = as;
  return (
    <Tag ref={ref} className={className}
      style={{ ...style, opacity: show ? 1 : 0, transform: show ? "translate(0,0)" : `translate(${x}px,${y}px)`,
        transition: `opacity ${duration}s cubic-bezier(0.25,0.1,0.25,1) ${delay}s, transform ${duration}s cubic-bezier(0.25,0.1,0.25,1) ${delay}s` }}>
      {children}
    </Tag>
  );
}

/* ---------- Magnet (mouse-following) ---------- */
function Magnet({ children, padding = 120, strength = 4, className = "", style = {} }) {
  const ref = useRef(null);
  const [p, setP] = useState({ x: 0, y: 0 });
  const [active, setActive] = useState(false);
  useEffect(() => {
    const handle = (e) => {
      const el = ref.current; if (!el) return;
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
      const dx = e.clientX - cx, dy = e.clientY - cy;
      if (Math.abs(dx) < r.width / 2 + padding && Math.abs(dy) < r.height / 2 + padding) {
        setActive(true); setP({ x: dx / strength, y: dy / strength });
      } else { setActive(false); setP({ x: 0, y: 0 }); }
    };
    window.addEventListener("mousemove", handle);
    return () => window.removeEventListener("mousemove", handle);
  }, [padding, strength]);
  return (
    <div ref={ref} className={className}
      style={{ ...style, transform: `translate3d(${p.x}px,${p.y}px,0)`, transition: active ? "transform 0.3s ease-out" : "transform 0.6s ease-in-out", willChange: "transform" }}>
      {children}
    </div>
  );
}

/* ---------- AnimatedText (per-char scroll reveal) ---------- */
function AnimatedText({ text, as = "p", className = "", style = {} }) {
  const ref = useRef(null);
  const [prog, setProg] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const el = ref.current; if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      setProg(clamp((0.8 * vh - r.top) / (0.6 * vh + r.height), 0, 1));
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    onScroll();
    return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); };
  }, [text]);
  const s = String(text);
  const N = s.length;
  const tokens = s.split(/(\s+)/);
  let idx = 0;
  const Tag = as;
  return (
    <Tag ref={ref} className={className} style={{ overflowWrap: "break-word", ...style }}>
      {tokens.map((tok, ti) => {
        if (tok === "") return null;
        if (/^\s+$/.test(tok)) { idx += tok.length; return <span key={ti}> </span>; }
        const chars = [...tok];
        const start = idx;
        idx += chars.length;
        return (
          <span key={ti} style={{ whiteSpace: "nowrap" }}>
            {chars.map((c, ci) => <span key={ci} style={{ opacity: 0.18 + 0.82 * clamp(prog * N - (start + ci), 0, 1) }}>{c}</span>)}
          </span>
        );
      })}
    </Tag>
  );
}

/* ---------- Parallax wrapper (vertical translate on scroll) ---------- */
function useParallax(speed = 0.2) {
  const ref = useRef(null);
  const [off, setOff] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const el = ref.current; if (!el) return;
      const r = el.getBoundingClientRect();
      setOff((window.innerHeight - r.top) * speed);
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    onScroll();
    return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); };
  }, [speed]);
  return [ref, off];
}

/* ============ Buttons & typographic atoms ============ */
function PrimaryButton({ children, href = "#", onClick, className = "", onDark = false, style = {} }) {
  const fill = onDark ? "#F4F1EA" : "var(--foreground)";
  const ink = onDark ? "#1A2233" : "var(--background)";
  const Comp = onClick ? "button" : "a";
  return (
    <Comp href={onClick ? undefined : href} onClick={onClick}
      className={`mb-btn inline-flex items-center gap-2 rounded-full font-medium uppercase tracking-widest transition-all duration-200 ${className}`}
      style={{ background: fill, color: ink, textDecoration: "none", border: "none", cursor: "pointer", padding: "0.9rem 2rem", fontSize: "0.78rem", fontFamily: "var(--font-sans)", whiteSpace: "nowrap", ...style }}
      onMouseEnter={(e) => { e.currentTarget.style.background = "var(--primary)"; e.currentTarget.style.color = "var(--primary-foreground)"; e.currentTarget.style.transform = "translateY(-1px)"; }}
      onMouseLeave={(e) => { e.currentTarget.style.background = fill; e.currentTarget.style.color = ink; e.currentTarget.style.transform = "translateY(0)"; }}>
      {children}
    </Comp>
  );
}

function GhostButton({ children, href = "#", onClick, className = "", onDark = false, style = {} }) {
  const ink = onDark ? "#F4F1EA" : "var(--foreground)";
  const fill = onDark ? "#1A2233" : "var(--background)";
  const Comp = onClick ? "button" : "a";
  return (
    <Comp href={onClick ? undefined : href} onClick={onClick}
      className={`inline-flex items-center gap-2 rounded-full font-medium uppercase tracking-widest transition-all duration-200 ${className}`}
      style={{ border: `1px solid ${ink}`, color: ink, textDecoration: "none", background: "transparent", cursor: "pointer", padding: "0.85rem 1.9rem", fontSize: "0.78rem", fontFamily: "var(--font-sans)", whiteSpace: "nowrap", ...style }}
      onMouseEnter={(e) => { e.currentTarget.style.background = ink; e.currentTarget.style.color = fill; }}
      onMouseLeave={(e) => { e.currentTarget.style.background = "transparent"; e.currentTarget.style.color = ink; }}>
      {children}
    </Comp>
  );
}

function Eyebrow({ children, className = "", style = {} }) {
  return (
    <span className={`mb-eyebrow ${className}`}
      style={{ fontFamily: "var(--font-mono)", textTransform: "uppercase", letterSpacing: "0.18em", fontSize: "0.72rem", color: "var(--primary)", fontWeight: 500, ...style }}>
      {children}
    </span>
  );
}

/* Big editorial serif heading with the navy→gray ink-bleed gradient. */
function Display({ children, as = "h2", className = "", style = {}, gradient = true }) {
  const Tag = as;
  return (
    <Tag className={`${gradient ? "mb-display" : ""} ${className}`}
      style={{ fontFamily: "var(--font-serif)", fontWeight: 500, letterSpacing: "-0.02em", lineHeight: 1.02, ...style }}>
      {children}
    </Tag>
  );
}

function ArrowLink({ children, href = "#", className = "", style = {} }) {
  return (
    <a href={href} className={`mb-arrow inline-flex items-center gap-2 ${className}`}
      style={{ color: "var(--foreground)", textDecoration: "none", fontFamily: "var(--font-sans)", fontWeight: 500, ...style }}
      onMouseEnter={(e) => (e.currentTarget.style.color = "var(--primary)")}
      onMouseLeave={(e) => (e.currentTarget.style.color = "var(--foreground)")}>
      {children} <span className="mb-arrow-glyph" style={{ transition: "transform .2s var(--ease)" }}>→</span>
    </a>
  );
}

Object.assign(window, {
  clamp, getLang, setLang, useLang, useTx, getTheme, applyTheme, useTheme,
  FadeIn, Magnet, AnimatedText, useParallax, CountUp,
  PrimaryButton, GhostButton, Eyebrow, Display, ArrowLink,
});

/* ---------- CountUp (animates to a target when scrolled into view) ---------- */
function CountUp({ value, duration = 1.6, className = "", style = {} }) {
  const ref = useRef(null);
  const parsed = String(value);
  const mm = parsed.match(/(\d[\d\s,.]*)/);
  const hasNum = !!mm;
  const target = hasNum ? parseInt(mm[1].replace(/[\s,.]/g, ""), 10) : 0;
  const prefix = hasNum ? parsed.slice(0, mm.index) : "";
  const suffix = hasNum ? parsed.slice(mm.index + mm[1].length) : parsed;
  const [n, setN] = useState(0);
  useEffect(() => {
    if (!hasNum) return;
    const el = ref.current;
    if (!el) return;
    let raf = 0, started = false;
    const run = () => {
      const start = performance.now();
      const tick = (now) => {
        const p = clamp((now - start) / (duration * 1000), 0, 1);
        const eased = 1 - Math.pow(1 - p, 3);
        setN(Math.round(target * eased));
        if (p < 1) raf = requestAnimationFrame(tick);
      };
      raf = requestAnimationFrame(tick);
    };
    const check = () => {
      const r = el.getBoundingClientRect();
      if (!started && r.top < window.innerHeight - 30 && r.bottom > 0) { started = true; run(); return true; }
      return false;
    };
    if (check()) return () => cancelAnimationFrame(raf);
    const onScroll = () => { if (check()) window.removeEventListener("scroll", onScroll); };
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => { window.removeEventListener("scroll", onScroll); cancelAnimationFrame(raf); };
  }, [target, duration, hasNum]);
  return <span ref={ref} className={className} style={style}>{prefix}{hasNum ? n : ""}{suffix}</span>;
}
window.CountUp = CountUp;

/* ============================================================
   MailerLite integration.
   Configure once: set window.MB_MAILERLITE before lib.jsx loads,
   or edit the defaults below. Uses MailerLite's client-safe
   embedded-form JSONP endpoint (account ID + form ID — NOT the
   secret API key). Find these in MailerLite → Forms → Embedded.
   ============================================================ */
const MB_MAILERLITE = Object.assign({
  account: "",          // e.g. "1234567"
  newsletterForm: "",   // embedded form ID for the newsletter
  auditForm: "",        // embedded form ID for the brand-audit lead
}, window.MB_MAILERLITE || {});

async function mbSubscribe({ email, name = "", form = "newsletter", fields = {} } = {}) {
  if (!email) return { ok: false, error: "no-email" };
  const acc = MB_MAILERLITE.account;
  const formId = form === "audit" ? MB_MAILERLITE.auditForm : MB_MAILERLITE.newsletterForm;
  if (!acc || !formId) {
    console.warn("[MailerLite] not configured — set window.MB_MAILERLITE.account + form IDs to go live.");
    return { ok: false, skipped: true };
  }
  try {
    const body = new URLSearchParams();
    body.set("fields[email]", email);
    if (name) body.set("fields[name]", name);
    Object.entries(fields).forEach(([k, v]) => body.set(`fields[${k}]`, v));
    body.set("ml-submit", "1");
    body.set("anticsrf", "true");
    await fetch(`https://assets.mailerlite.com/jsonp/${acc}/forms/${formId}/subscribe`, {
      method: "POST", mode: "no-cors", body,
    });
    return { ok: true };
  } catch (e) {
    console.warn("[MailerLite] subscribe failed:", e);
    return { ok: false, error: String(e) };
  }
}
window.MB_MAILERLITE = MB_MAILERLITE;
window.mbSubscribe = mbSubscribe;
