// landing/polish.jsx
// Cinematic motion add-ons that build on motion.jsx primitives.
//
// CLASS-BASED reveals (v2). Defines transitions ONCE in CSS so React re-renders
// that recreate the inline style object don't restart the transition mid-flight.
// Each Reveal exposes its timing as CSS custom properties on a wrapper element
// and flips a single class (`.is-visible`) to commit the reveal.
//
// Exports (via window):
//   WordReveal     — splits a string into words and fades them in sequentially
//   BlurReveal     — like Reveal, but reveals from a blur (4px → 0)
//   VideoBackdrop  — looping muted <video> with color overlay + vignette
//   ParallaxY      — translateY tied to scroll, gentler than useParallax
//   ShimmerText    — gold sweep on text
//   EASE_SMOOTH    — extra-smooth "iOS" cubic-bezier
//   EASE_OUT_QUINT — sharper attack, soft tail

const { useEffect: pUseEffect, useRef: pUseRef, useState: pUseState, useMemo: pUseMemo } = React;

const EASE_SMOOTH    = 'cubic-bezier(0.22, 1, 0.36, 1)';
const EASE_OUT_QUINT = 'cubic-bezier(0.22, 1, 0.36, 1)';
const EASE_GENTLE    = 'cubic-bezier(0.4, 0, 0.2, 1)';

// ─── BlurReveal ──────────────────────────────────────────────────
// Drop-in for Reveal that reveals from a soft blur. CSS-class-driven so
// re-renders of the parent don't restart the transition.
const BlurReveal = React.memo(function BlurReveal({
  children, delay = 0, dy = 24, dx = 0, duration = 900,
  blur = 6, threshold = 0.15, as: As = 'div', style, className = '', ...rest
}) {
  const reduced = useReducedMotion();
  const [ref, visible] = useReveal({ threshold });
  const show = visible || reduced;
  const cssVars = pUseMemo(() => ({
    '--rv-delay': `${delay}ms`,
    '--rv-duration': `${duration}ms`,
    '--rv-dx': `${dx}px`,
    '--rv-dy': `${dy}px`,
    '--rv-blur': `${blur}px`,
    ...style,
  }), [delay, duration, dx, dy, blur, style]);
  return (
    <As ref={ref}
      className={`antee-reveal antee-reveal-blur${show ? ' is-visible' : ''} ${className}`.trim()}
      data-visible={show ? 'true' : 'false'}
      style={cssVars}
      {...rest}>{children}</As>
  );
});

// ─── WordReveal ──────────────────────────────────────────────────
// Splits text into words and fades them in sequentially via CSS classes.
// Per-token style objects are memoized so re-renders don't restart the
// CSSTransition (Chrome resets currentTime when an inline CSS variable is
// re-set even to the same string).
function WordReveal({
  text, children,
  delay = 0, step = 70, dy = 18, duration = 800, blur = 4,
  className = '', style, as: As = 'span', ...rest
}) {
  const reduced = useReducedMotion();
  const [ref, visible] = useReveal({ threshold: 0.1 });
  const show = visible || reduced;

  // Precompute tokens + their per-token style ONCE per param-set.
  // The style objects survive across re-renders so React doesn't re-apply
  // the inline `style` attribute (which would restart the transition).
  const prepared = pUseMemo(() => {
    const items = [];
    const src = text != null ? text : children;
    const walk = (node) => {
      if (node == null || node === false) return;
      if (Array.isArray(node)) { node.forEach(walk); return; }
      if (typeof node === 'string') {
        const parts = node.split(/(\s+)/).filter(p => p.length);
        parts.forEach(p => items.push(p));
        return;
      }
      items.push(node);
    };
    walk(src);

    let visibleIndex = 0;
    return items.map((tok) => {
      const isSpace = typeof tok === 'string' && /^\s+$/.test(tok);
      if (isSpace) return { tok, space: true };
      const idx = visibleIndex++;
      const tDelay = delay + idx * step;
      return {
        tok,
        space: false,
        style: {
          '--rv-delay': `${tDelay}ms`,
          '--rv-duration': `${duration}ms`,
          '--rv-dy': `${dy}px`,
          '--rv-blur': `${blur}px`,
        },
      };
    });
  }, [text, children, delay, step, duration, dy, blur]);

  return (
    <As ref={ref} className={className} style={{ display: 'inline', ...style }} {...rest}>
      {prepared.map((p, i) => {
        if (p.space) return <React.Fragment key={i}>{p.tok}</React.Fragment>;
        return (
          <span key={i}
            className={`antee-reveal antee-reveal-word${show ? ' is-visible' : ''}`}
            style={p.style}>
            {p.tok}
          </span>
        );
      })}
    </As>
  );
}

// ─── VideoBackdrop ───────────────────────────────────────────────
function VideoBackdrop({
  src, poster, overlay, vignette = true, opacity = 1,
  blendMode = 'normal', filter = 'none', style, children,
}) {
  const reduced = useReducedMotion();
  const ref = pUseRef(null);
  const [ready, setReady] = pUseState(false);

  pUseEffect(() => {
    const v = ref.current;
    if (!v) return;
    if (reduced) return;
    v.playbackRate = 0.85;
    const tryPlay = () => v.play().catch(() => {});
    if (v.readyState >= 2) { setReady(true); tryPlay(); }
    const onCanPlay = () => { setReady(true); tryPlay(); };
    v.addEventListener('canplay', onCanPlay);
    return () => v.removeEventListener('canplay', onCanPlay);
  }, [reduced, src]);

  return (
    <div style={{
      position: 'absolute', inset: 0, overflow: 'hidden',
      pointerEvents: 'none', ...style,
    }}>
      {!reduced && src && (
        <video
          ref={ref}
          src={src}
          poster={poster}
          autoPlay muted loop playsInline preload="auto"
          style={{
            position: 'absolute', inset: 0,
            width: '100%', height: '100%', objectFit: 'cover',
            opacity: ready ? opacity : 0,
            mixBlendMode: blendMode,
            filter,
            transition: `opacity 900ms ${EASE_SMOOTH}`,
            willChange: 'opacity',
          }}
        />
      )}
      {reduced && poster && (
        <img src={poster} alt=""
          style={{
            position: 'absolute', inset: 0,
            width: '100%', height: '100%', objectFit: 'cover',
            opacity, mixBlendMode: blendMode,
          }}/>
      )}
      {overlay && (
        <div style={{
          position: 'absolute', inset: 0,
          background: overlay,
          pointerEvents: 'none',
        }}/>
      )}
      {vignette && (
        <div style={{
          position: 'absolute', inset: 0,
          background: 'radial-gradient(ellipse 100% 100% at 50% 40%, transparent 30%, rgba(0,0,0,0.18) 100%)',
          pointerEvents: 'none',
        }}/>
      )}
      {children}
    </div>
  );
}

// ─── ParallaxY ───────────────────────────────────────────────────
// Direct DOM mutation — does not re-render the React tree on scroll.
function ParallaxY({ children, speed = 0.12, style, ...rest }) {
  const reduced = useReducedMotion();
  const wrapRef = pUseRef(null);
  const innerRef = pUseRef(null);
  pUseEffect(() => {
    if (reduced || !wrapRef.current || !innerRef.current) return;
    const outer = wrapRef.current;
    const inner = innerRef.current;
    let raf = null;
    const update = () => {
      const rect = outer.getBoundingClientRect();
      const center = rect.top + rect.height / 2;
      const vh = window.innerHeight || 800;
      const delta = (center - vh / 2);
      const y = -delta * speed;
      inner.style.transform = `translate3d(0, ${y.toFixed(1)}px, 0)`;
      raf = null;
    };
    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);
    };
  }, [speed, reduced]);
  return (
    <div ref={wrapRef} style={style} {...rest}>
      <div ref={innerRef} style={{ willChange: reduced ? 'auto' : 'transform' }}>
        {children}
      </div>
    </div>
  );
}

// ─── ShimmerText ─────────────────────────────────────────────────
function ShimmerText({ children, color = '#C9A84C', duration = 2400, repeat = false, style }) {
  return (
    <span style={{
      backgroundImage: `linear-gradient(90deg, ${color} 0%, ${color} 30%, rgba(255,240,210,0.95) 50%, ${color} 70%, ${color} 100%)`,
      backgroundSize: '300% 100%',
      backgroundPosition: '100% 0',
      WebkitBackgroundClip: 'text', backgroundClip: 'text',
      WebkitTextFillColor: 'transparent', color: 'transparent',
      animation: `anteeShimmerSweep ${duration}ms ${EASE_SMOOTH} ${repeat ? 'infinite' : '1'} forwards`,
      display: 'inline-block',
      ...style,
    }}>{children}</span>
  );
}

// ─── inject reveal + polish keyframes ────────────────────────────
(function injectPolishCSS() {
  if (typeof document === 'undefined') return;
  if (document.getElementById('antee-polish-keyframes')) return;
  const style = document.createElement('style');
  style.id = 'antee-polish-keyframes';
  style.textContent = `
    /* ── class-based reveals (replaces inline-style transitions) ── */
    .antee-reveal {
      opacity: 0;
      transform: translate3d(var(--rv-dx, 0px), var(--rv-dy, 24px), 0);
      transition:
        opacity   var(--rv-duration, 700ms) cubic-bezier(0.22, 1, 0.36, 1) var(--rv-delay, 0ms),
        transform var(--rv-duration, 700ms) cubic-bezier(0.22, 1, 0.36, 1) var(--rv-delay, 0ms),
        filter    var(--rv-duration, 700ms) cubic-bezier(0.22, 1, 0.36, 1) var(--rv-delay, 0ms);
      will-change: opacity, transform, filter;
    }
    .antee-reveal-blur,
    .antee-reveal-word {
      filter: blur(var(--rv-blur, 0px));
    }
    .antee-reveal-word {
      display: inline-block;
      /* keep whitespace between words even though tokens are inline-block */
      white-space: pre;
    }
    .antee-reveal.is-visible {
      opacity: 1;
      transform: translate3d(0, 0, 0);
      filter: blur(0);
    }
    @media (prefers-reduced-motion: reduce) {
      .antee-reveal {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        transition: none !important;
      }
    }

    @keyframes anteeShimmerSweep {
      0%   { background-position: 100% 0; }
      100% { background-position: -50% 0; }
    }
    @keyframes anteeUnderlineDraw {
      0%   { stroke-dashoffset: 220; opacity: 0; }
      40%  { opacity: 0.7; }
      100% { stroke-dashoffset: 0; opacity: 0.7; }
    }
    @keyframes anteeDriftSlow {
      0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
      50%      { transform: translate3d(-8px, 6px, 0) scale(1.03); }
    }
    @keyframes anteeFadeIn {
      from { opacity: 0; }
      to   { opacity: 1; }
    }
  `;
  document.head.appendChild(style);
})();

Object.assign(window, {
  WordReveal, BlurReveal, VideoBackdrop, ParallaxY, ShimmerText,
  EASE_SMOOTH, EASE_OUT_QUINT, EASE_GENTLE,
});
