// Big "ARNEL" wordmark that drops a trail of Life Off Screen photos as the
// cursor moves across it (SVG text used as a mask over the dropped photos).
//
// How it works: the same word is drawn twice — once as the neon-green fill
// you see at rest, and once (in white) inside an SVG <mask>. Photos are
// appended into a masked <g>, so they can only ever paint inside the
// letterforms. Every time the pointer travels far enough, the next photo in
// the set is dropped at the cursor, pops in, lingers, then fades out.
//
// Drops are created imperatively (not via React state) so a fast-moving
// cursor never triggers a re-render storm.
function NameReveal() {
  const svgRef = React.useRef(null);
  const layerRef = React.useRef(null);
  const [touched, setTouched] = React.useState(false);

  // Photos come straight from the Life Off Screen data.
  const shots = (window.SITE_DATA.lifestyle || []).map((l) => l.img);

  React.useEffect(() => {
    const svg = svgRef.current;
    const layer = layerRef.current;
    const defs = svg && svg.querySelector('defs');
    if (!svg || !layer || !defs || !shots.length) return;

    const SVG_NS = 'http://www.w3.org/2000/svg';
    const VB_W = 1200;   // viewBox width  (must match the <svg> below)
    const VB_H = 260;    // viewBox height
    const STEP = 70;     // viewBox units the cursor must travel per drop
    const LIFE = 1100;   // ms a photo stays fully visible
    const MAX = 16;      // hard cap on live photos
    const CORNER = 18;   // rounded-corner radius, in viewBox px
    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    let lastX = null;
    let lastY = null;
    let idx = 0;
    let clipSeq = 0;
    const live = [];

    // Each dropped photo gets its OWN clipPath (userSpaceOnUse, matching
    // its exact x/y/w/h) rather than one shared objectBoundingBox
    // clipPath — Chromium ignores rx/ry on rects inside
    // objectBoundingBox clipPaths, so a shared one clips the rectangle
    // but silently drops the rounding.
    const removeWithClip = (img) => {
      img.remove();
      if (img.__clip) img.__clip.remove();
    };

    const drop = (x, y) => {
      const w = 300;
      const h = 230;
      const left = x - w / 2;
      const top = y - h / 2;

      const clipId = 'nr-clip-' + (clipSeq++);
      const clip = document.createElementNS(SVG_NS, 'clipPath');
      clip.id = clipId;
      const rect = document.createElementNS(SVG_NS, 'rect');
      rect.setAttribute('x', left);
      rect.setAttribute('y', top);
      rect.setAttribute('width', w);
      rect.setAttribute('height', h);
      rect.setAttribute('rx', CORNER);
      rect.setAttribute('ry', CORNER);
      clip.appendChild(rect);
      defs.appendChild(clip);

      const img = document.createElementNS(SVG_NS, 'image');
      img.setAttribute('href', shots[idx % shots.length]);
      img.setAttribute('x', left);
      img.setAttribute('y', top);
      img.setAttribute('width', w);
      img.setAttribute('height', h);
      img.setAttribute('preserveAspectRatio', 'xMidYMid slice');
      img.setAttribute('class', 'nr-drop');
      img.setAttribute('clip-path', 'url(#' + clipId + ')');
      img.__clip = clip;
      // Alternate the tilt so the trail feels hand-tossed, not mechanical.
      img.style.setProperty('--rot', (idx % 2 ? 1 : -1) * (3 + (idx % 4) * 1.6) + 'deg');
      idx++;

      layer.appendChild(img);
      live.push(img);

      // Pop in on the next frame so the transition actually runs.
      requestAnimationFrame(() => img.classList.add('is-in'));

      // Retire it: fade out, then remove from the DOM.
      const t = setTimeout(() => {
        img.classList.remove('is-in');
        img.classList.add('is-out');
        setTimeout(() => {
          removeWithClip(img);
          const i = live.indexOf(img);
          if (i > -1) live.splice(i, 1);
        }, 500);
      }, reduced ? 400 : LIFE);
      img.__timer = t;

      // Trim the oldest if the cursor is racing around.
      while (live.length > MAX) {
        const old = live.shift();
        clearTimeout(old.__timer);
        removeWithClip(old);
      }
    };

    const onMove = (e) => {
      const r = svg.getBoundingClientRect();
      if (!r.width) return;
      const x = ((e.clientX - r.left) / r.width) * VB_W;
      const y = ((e.clientY - r.top) / r.height) * VB_H;

      if (lastX === null) { lastX = x; lastY = y; drop(x, y); setTouched(true); return; }
      const dx = x - lastX;
      const dy = y - lastY;
      if (Math.sqrt(dx * dx + dy * dy) < STEP) return; // not far enough yet

      lastX = x;
      lastY = y;
      drop(x, y);
      setTouched(true);
    };

    const onLeave = () => { lastX = null; lastY = null; };

    svg.addEventListener('pointermove', onMove);
    svg.addEventListener('pointerleave', onLeave);

    // Touch devices have no cursor to move, so the trail plays itself while
    // the wordmark is on screen (a drag across it still drops photos too).
    let autoTimer = null;
    let io = null;
    const isTouch = !window.matchMedia('(hover: hover)').matches;
    if (isTouch) {
      let t = 0;
      const tick = () => {
        t += 1;
        const p = (t % 24) / 24;                                   // sweep left -> right
        const x = 70 + (VB_W - 140) * p;
        const y = VB_H * (0.5 + 0.2 * Math.sin(t / 2.1));          // gentle wave
        drop(x, y);
        setTouched(true);
      };
      io = new IntersectionObserver((entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting && !autoTimer) { tick(); autoTimer = setInterval(tick, 340); }
          else if (!e.isIntersecting && autoTimer) { clearInterval(autoTimer); autoTimer = null; }
        });
      }, { threshold: 0.3 });
      io.observe(svg);
    }

    return () => {
      svg.removeEventListener('pointermove', onMove);
      svg.removeEventListener('pointerleave', onLeave);
      if (autoTimer) clearInterval(autoTimer);
      if (io) io.disconnect();
      live.forEach((el) => { clearTimeout(el.__timer); removeWithClip(el); });
    };
  }, [shots.length]);

  return (
    <section className="section dark name-reveal-section" data-screen-label="Name Reveal">
      <div className="container">
        <Reveal><div className="eyebrow on-dark"><b>/</b> THE PERSON BEHIND THE WORK</div></Reveal>
      </div>
      <div className="name-reveal-bleed">
        <Reveal>
          <div className={`name-reveal ${touched ? 'is-touched' : ''}`} role="img"
               aria-label="Arnel — move your cursor across the name to reveal photos">
            <svg ref={svgRef} className="name-reveal-svg" viewBox="0 0 1200 260"
                 preserveAspectRatio="xMidYMid meet">
              <defs>
                <mask id="nr-letter-mask">
                  {/* black hides, white reveals */}
                  <rect x="0" y="0" width="1200" height="260" fill="#000" />
                  <text className="nr-word" x="600" y="242" textAnchor="middle"
                        textLength="1180" lengthAdjust="spacingAndGlyphs" fill="#fff">ARNEL</text>
                </mask>
                {/* Dropped-photo clipPaths (one per live photo) are
                    injected here at runtime — see drop() below. Plain CSS
                    border-radius doesn't reliably round SVG <image>
                    corners, and a single shared objectBoundingBox
                    clipPath doesn't work either: Chromium ignores rx/ry
                    on rects inside objectBoundingBox clipPaths (the clip
                    applies, the rounding silently doesn't). A per-photo
                    userSpaceOnUse clipPath with absolute pixel rx/ry
                    sidesteps that bug and is universally supported. */}
              </defs>

              {/* Resting state: the neon-green wordmark */}
              <text className="nr-word nr-fill" x="600" y="242" textAnchor="middle"
                    textLength="1180" lengthAdjust="spacingAndGlyphs">ARNEL</text>

              {/* Dropped photos land here, clipped to the letterforms */}
              <g ref={layerRef} className="nr-photos" mask="url(#nr-letter-mask)"></g>
            </svg>
            <span className="name-reveal-hint">move your cursor</span>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

window.NameReveal = NameReveal;
