/* global React */

/* ============================================================
   Willow kit: the pieces every page is built from.
   Scroll hooks, reveals, the painted page hero, photo frames,
   section heads and the closing card.
   ============================================================ */

const wlClamp = (v) => Math.min(1, Math.max(0, v));
const WL_RM = typeof window !== "undefined" &&
  window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;

/* Calls fn(progress, rect, vh) on every animation frame the page scrolls.
   progress runs 0 → 1 across a tall (sticky) section, or across the
   element's pass through the viewport when it is shorter than one screen. */
function useScrollP(ref, fn) {
  const cb = React.useRef(fn);
  cb.current = fn;
  React.useEffect(() => {
    let raf = 0;
    const run = () => {
      raf = 0;
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const total = r.height - vh;
      const p = total > 1 ? wlClamp(-r.top / total) : wlClamp((vh - r.top) / (vh + r.height));
      cb.current(p, r, vh);
    };
    const on = () => { if (!raf) raf = requestAnimationFrame(run); };
    run();
    window.addEventListener("scroll", on, { passive: true });
    window.addEventListener("resize", on);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("scroll", on);
      window.removeEventListener("resize", on);
    };
  }, []);
}

/* Flips to true once the element has been seen. */
function useSeen(threshold = 0.2) {
  const ref = React.useRef(null);
  const [seen, setSeen] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (WL_RM) { setSeen(true); return; }
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setSeen(true); io.disconnect(); }
    }, { threshold, rootMargin: "0px 0px -6% 0px" });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return [ref, seen];
}

const scrollToId = (id) => {
  const el = document.getElementById(id);
  if (el) el.scrollIntoView({ behavior: WL_RM ? "auto" : "smooth", block: "start" });
};

const ART = {
  willow:   "images/art/willow-monet.jpg",
  birch:    "images/art/birch.jpg",
  pine:     "images/art/signac-pine.jpg",
  wheat:    "images/art/wheat-cypress.jpg",
  green:    "images/art/green-field.jpg",
  poplars:  "images/art/poplars.jpg",
  olive:    "images/art/olive-trees.jpg",
  lilies:   "images/art/lilies.jpg",
  calanque: "images/art/calanque.jpg",
  bathers:  "images/art/seurat-bathers.jpg",
  roots:    "images/art/roots.jpg",
};

/* Fades and lifts its children in once, when scrolled into view. */
function Rise({ as: Tag = "div", delay = 0, className = "", style, children, ...rest }) {
  const [ref, seen] = useSeen(0.14);
  return (
    <Tag ref={ref} className={`rise ${seen ? "in" : ""} ${className}`} style={{ transitionDelay: `${delay}ms`, ...style }} {...rest}>
      {children}
    </Tag>
  );
}

/* Section heading: numbered kicker, serif title, optional aside. */
function SecHead({ kicker, title, sub, center = false }) {
  return (
    <Rise className={`sh ${center ? "center" : ""}`}>
      <span className="kicker">{kicker}</span>
      <h2>{title}</h2>
      {sub && <p className="sh-sub">{sub}</p>}
    </Rise>
  );
}

/* ============================================================
   Page hero: words rise, the painting unrolls beside them.
   lines: ["First line.", "Second, in italic."]
   ============================================================ */
function PageHero({ kicker, lines, lede, art, children }) {
  const [on, setOn] = React.useState(WL_RM);
  const ref = React.useRef(null);
  const artRef = React.useRef(null);
  React.useEffect(() => {
    if (WL_RM) return;
    const t = setTimeout(() => setOn(true), 40);
    return () => clearTimeout(t);
  }, []);
  useScrollP(ref, (p, r, vh) => {
    if (WL_RM || !artRef.current) return;
    const q = wlClamp(-r.top / vh);
    artRef.current.style.transform = `scale(${1.06 + q * 0.1}) translate3d(0, ${q * 7}%, 0)`;
  });
  let wi = 0;
  return (
    <header ref={ref} className={`ph ${on ? "on" : ""}`}>
      <div className="ph-text">
        <span className="kicker ph-kick">{kicker}</span>
        <h1 className="ph-title">
          {lines.map((ln, li) => (
            <span className={`ph-line ${li > 0 ? "em" : ""}`} key={li}>
              {ln.split(" ").map((w, k) => (
                <span className="ph-w" key={k}><span style={{ transitionDelay: `${140 + (wi++) * 60}ms` }}>{w}</span></span>
              ))}
            </span>
          ))}
        </h1>
        {lede && <p className="ph-lede">{lede}</p>}
        {children && <div className="ph-actions">{children}</div>}
      </div>
      <div className="ph-art grain">
        <img ref={artRef} src={art} alt="" fetchpriority="high" />
      </div>
    </header>
  );
}

/* ============================================================
   Frames for real photos
   kind: polaroid · tape · print · plain
   ============================================================ */
function Frame({ src, cap, kind = "polaroid", tilt = 0, ratio = "4/3", pos = "center", className = "", style }) {
  return (
    <figure className={`frame frame-${kind} ${className}`} style={{ "--tilt": `${tilt}deg`, ...style }}>
      <div className="frame-img" style={{ aspectRatio: ratio }}>
        <img src={src} alt={cap || ""} loading="lazy" style={{ objectPosition: pos }} />
      </div>
      {kind === "tape" && <><i className="tape t1" /><i className="tape t2" /></>}
      {cap && <figcaption>{cap}</figcaption>}
    </figure>
  );
}

/* ============================================================
   Closing card: a cream card resting on a blurred painting
   ============================================================ */
function PageCloser({ kicker, title, sub, buttons = [], art = ART.calanque, bg = ART.bathers }) {
  const ref = React.useRef(null);
  const cardRef = React.useRef(null);
  const bgRef = React.useRef(null);
  useScrollP(ref, (p) => {
    if (WL_RM) return;
    const e = wlClamp(p * 1.8);
    if (cardRef.current) {
      cardRef.current.style.transform = `translate3d(0, ${(1 - e) * 60}px, 0) scale(${0.94 + e * 0.06})`;
      cardRef.current.style.opacity = String(0.25 + e * 0.75);
    }
    if (bgRef.current) bgRef.current.style.transform = `scale(${1.18 - p * 0.1})`;
  });
  return (
    <section className="join closer" ref={ref}>
      <div className="join-bg" aria-hidden="true"><img ref={bgRef} src={bg} alt="" loading="lazy" /></div>
      <div className="join-card" ref={cardRef}>
        <div className="join-art grain"><img src={art} alt="" loading="lazy" /></div>
        <div className="join-body">
          <span className="kicker">{kicker}</span>
          <h2>{title}</h2>
          {sub && <p className="join-sub">{sub}</p>}
          <div className="join-ctas">
            {buttons.map((b, i) => (
              <button key={i} className={`pill-btn ${b.ghost ? "ghost" : ""}`} onClick={b.onClick}>
                {b.label}{!b.ghost && <span aria-hidden="true">→</span>}
              </button>
            ))}
          </div>
          <div className="join-sig">
            <LeafGlyph size={34} />
            <span className="join-sig-name">Willow</span>
            <span className="join-sig-line">360 members · 5 states · 3 countries<br />7 university partners</span>
          </div>
        </div>
      </div>
    </section>
  );
}

/* University logos, drifting slowly */
const LOGOS = [
  ["images/logo-hms.png", "Harvard Medical School"],
  ["images/logo-yale.svg", "Yale"],
  ["images/logo-usc.svg", "USC"],
  ["images/logo-jhu.png", "Johns Hopkins"],
  ["images/logo-rutgers.svg", "Rutgers"],
  ["images/logo-uams.png", "UAMS"],
];

function Partners({ label = "Mentored by faculty at" }) {
  const row = LOGOS.concat(LOGOS);
  return (
    <section className="partners" aria-label="University partners">
      <span className="partners-label">{label}</span>
      <div className="marquee">
        <div className="marquee-track">
          {row.map(([src, alt], i) => <img key={i} src={src} alt={i < LOGOS.length ? alt : ""} aria-hidden={i >= LOGOS.length} loading="lazy" />)}
        </div>
      </div>
    </section>
  );
}

Object.assign(window, {
  wlClamp, WL_RM, useScrollP, useSeen, scrollToId, ART,
  Rise, SecHead, PageHero, Frame, PageCloser, Partners,
});
