/* ============================================================
   Shared components + scroll hooks
   ============================================================ */
const { useState, useEffect, useRef, useCallback } = React;

/* ---------- Gray placeholder (or real image when src is set) ---------- */
function Placeholder({ tone = "1", label, parallax = false, zoom = false, speed = 0.12, style, className = "", src }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!parallax) return;
    const el = ref.current;
    if (!el) return;
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduce) return;
    let raf = null;
    const inner = el.querySelector(".ph__inner");
    const update = () => {
      raf = null;
      const amount = window.__PARALLAX_AMOUNT ?? 1;
      if (!inner) return;
      if (amount === 0) { inner.style.transform = ""; return; }
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const center = r.top + r.height / 2;
      const prog = (center - vh / 2) / (vh / 2 + r.height / 2); // -1..1, 0 centered
      const ty = -prog * speed * 100 * amount;
      let tr = `translate3d(0, ${ty.toFixed(2)}px, 0)`;
      if (zoom) {
        const sc = 1 + Math.min(0.09, Math.abs(prog) * 0.09);
        tr += ` scale(${sc.toFixed(3)})`;
      }
      inner.style.transform = tr;
    };
    const onScroll = () => { if (raf == null) raf = requestAnimationFrame(update); };
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [parallax, zoom, speed]);

  return (
    <div ref={ref} className={"ph " + className + (src ? " ph--img" : "")} data-tone={tone} style={style}>
      <div className="ph__inner">
        {src && <img src={src} alt={label || ""} className="ph__img" />}
      </div>
      {label && !src ? <span className="ph__label">{label}</span> : null}
    </div>
  );
}

/* ---------- Reveal-on-scroll wrapper ---------- */
function Reveal({ children, as = "div", delay = 0, className = "", style, fromRight = false }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduce) { setShown(true); return; }
    let done = false;
    let raf = null;
    const check = () => {
      raf = null;
      if (done || !ref.current) return;
      const r = ref.current.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      // in view once its top crosses ~94% of viewport height
      if (r.top < vh * 0.94 && r.bottom > 0) {
        done = true;
        setTimeout(() => setShown(true), delay);
        window.removeEventListener("scroll", onScroll);
        window.removeEventListener("resize", onScroll);
      }
    };
    const onScroll = () => { if (raf == null) raf = requestAnimationFrame(check); };
    // run once synchronously (rAF is throttled in offscreen iframes)
    check();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [delay]);
  const Tag = as;
  return <Tag ref={ref} className={"reveal " + (fromRight ? "reveal--right " : "") + (shown ? "in " : "") + className} style={style}>{children}</Tag>;
}

/* ---------- Social icons (line) ---------- */
const ICONS = {
  instagram: <><rect x="3" y="3" width="18" height="18" rx="5" /><circle cx="12" cy="12" r="4" /><circle cx="17.5" cy="6.5" r="0.6" fill="currentColor" /></>,
  dribbble: <><circle cx="12" cy="12" r="9" /><path d="M5 8c4 1 9 1 13-1M3.5 13c5-1 9 1 12 6M9 3.5c4 5 5 11 4 16" /></>,
  linkedin: <><rect x="3" y="3" width="18" height="18" rx="3" /><path d="M7 10v7M7 7v.01M11 17v-4a2 2 0 0 1 4 0v4" /></>,
  read: <><path d="M5 4h10a2 2 0 0 1 2 2v14l-4-3-4 3V6a2 2 0 0 0-2-2H5z" /><path d="M5 4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h2" /></>,
};
function SocialIcon({ name }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
      {ICONS[name]}
    </svg>
  );
}
function Socials({ names = ["instagram", "dribbble", "linkedin", "read"] }) {
  return (
    <div className="socials">
      {names.map((n) => (
        <a key={n} className="social" href="#" aria-label={n} onClick={(e) => e.preventDefault()}>
          <SocialIcon name={n} />
        </a>
      ))}
    </div>
  );
}

/* ---------- Lang toggle (top-right of card) ---------- */
function LangToggle({ lang, setLang }) {
  return (
    <button
      className="lang-toggle"
      onClick={() => setLang(lang === "fr" ? "en" : "fr")}
      aria-label="Language"
    >
      <span className={lang === "fr" ? "on" : ""}>FR</span>
      <span className="sep">/</span>
      <span className={lang === "en" ? "on" : ""}>EN</span>
    </button>
  );
}

/* ---------- Back button (circle arrow) ---------- */
function BackButton({ onClick, label }) {
  return (
    <button className="backbtn" onClick={onClick} aria-label={label}>
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
        <path d="M15 6l-6 6 6 6" />
      </svg>
    </button>
  );
}

Object.assign(window, { Placeholder, Reveal, Socials, SocialIcon, LangToggle, BackButton });
