const { useCallback, useEffect, useRef } = React;

const _seClamp = (v, a, b) => (v < a ? a : v > b ? b : v);
const _seSmoothstep = (edge0, edge1, x) => {
  const t = _seClamp((x - edge0) / (edge1 - edge0 || 1e-6), 0, 1);
  return t * t * (3 - 2 * t);
};

function ScrollExpand({
  src = '', mediaType = 'image', poster = '', alt = '',
  title = '', scrollHint = '',
  startWidth = 42, startHeight = 58, startRadius = 24, endRadius = 0,
  mediaZoom = 1.35, scrollDistance = 1.2,
  smoothing = 0.1, overlayScrim = 0.45,
  enabled = true,
  children, className = '', style, ...rest
}) {
  const rootRef = useRef(null);
  const trackRef = useRef(null);
  const stageRef = useRef(null);
  const frameRef = useRef(null);
  const mediaRef = useRef(null);
  const scrimRef = useRef(null);
  const hintRef = useRef(null);

  const propsRef = useRef({});
  propsRef.current = {
    startWidth, startHeight, startRadius, endRadius,
    mediaZoom, scrollDistance, smoothing,
    overlayScrim, enabled
  };

  const wasAtFullRef = useRef(false);
  const PLAY_THRESHOLD = 0.995;
  const REARM_THRESHOLD = 0.9;

  const applyProgress = useCallback((p) => {
    const frame = frameRef.current;
    const media = mediaRef.current;
    if (!frame || !media) return;
    const c = propsRef.current;

    if (p > 0.997) p = 1;

    const e = _seSmoothstep(0, 1, p);
    const w = c.startWidth + (100 - c.startWidth) * e;
    const h = c.startHeight + (100 - c.startHeight) * e;
    const ix = Math.max(0, (100 - w) / 2);
    const iy = Math.max(0, (100 - h) / 2);
    const r = c.startRadius + (c.endRadius - c.startRadius) * e;
    if (p >= 1) {
      frame.style.clipPath = 'none';
    } else {
      frame.style.clipPath =
        `inset(${iy.toFixed(3)}% ${ix.toFixed(3)}% ${iy.toFixed(3)}% ${ix.toFixed(3)}% round ${r.toFixed(2)}px)`;
    }
    media.style.transform = `scale(${(c.mediaZoom + (1 - c.mediaZoom) * e).toFixed(4)})`;
    if (scrimRef.current) scrimRef.current.style.opacity = `${(c.overlayScrim * e).toFixed(3)}`;

    if (hintRef.current) {
      const hintOpacity = _seClamp(1 - p / 0.12, 0, 1);
      hintRef.current.style.opacity = hintOpacity.toFixed(3);
      hintRef.current.style.transform = `translateY(${(-8 * (1 - hintOpacity)).toFixed(1)}px)`;
    }

    if (mediaType === 'video' && media.tagName === 'VIDEO') {
      if (!wasAtFullRef.current && p >= PLAY_THRESHOLD) {
        wasAtFullRef.current = true;
        try { media.currentTime = 0; } catch (_) {}
        const pr = media.play();
        if (pr && typeof pr.catch === 'function') pr.catch(() => {});
      } else if (wasAtFullRef.current && p < REARM_THRESHOLD) {
        wasAtFullRef.current = false;
      }
    }
  }, [mediaType]);

  useEffect(() => {
    const track = trackRef.current;
    const stage = stageRef.current;
    if (!track || !stage) return;

    let raf = 0;
    let current = 0;
    let target = 0;
    let stageH = 0;
    let running = false;

    let hasMeasured = false;
    const measure = () => {
      const nextH = window.innerHeight;
      if (hasMeasured && Math.abs(nextH - stageH) < 80) {
        return;
      }
      stageH = nextH;
      hasMeasured = true;
      stage.style.height = `${stageH}px`;
      const c = propsRef.current;
      const SD = Math.max(0.01, c.scrollDistance);
      track.style.height = `${stageH * (1 + SD)}px`;
    };

    const readProgress = () => {
      const c = propsRef.current;
      if (!c.enabled) return 1;
      const SD = Math.max(0.01, c.scrollDistance);
      const trackTop = track.getBoundingClientRect().top;
      const t = -trackTop / stageH;
      return _seClamp(t / SD, 0, 1);
    };

    const tick = () => {
      const c = propsRef.current;
      const k = c.smoothing <= 0 ? 1 : 1 - Math.exp(-1 / (60 * c.smoothing));
      current += (target - current) * k;
      if (Math.abs(target - current) < 0.0004) { current = target; running = false; }
      applyProgress(current);
      raf = running ? requestAnimationFrame(tick) : 0;
    };
    const kick = () => {
      if (running) return;
      running = true;
      if (!raf) raf = requestAnimationFrame(tick);
    };
    const onScroll = () => { target = readProgress(); kick(); };
    const onResize = () => { measure(); onScroll(); };

    measure();
    target = readProgress();
    current = target;
    applyProgress(current);

    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onResize, { passive: true });

    return () => {
      if (raf) cancelAnimationFrame(raf);
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onResize);
    };
  }, [applyProgress]);

  return (
    <div ref={rootRef} className={`scroll-expand ${className}`} style={style} {...rest}>
      <div ref={trackRef} className="scroll-expand__track">
        <div ref={stageRef} className="scroll-expand__stage">
          <div ref={frameRef} className="scroll-expand__frame">
            {mediaType === 'video' ? (
              <video
                ref={mediaRef}
                className="scroll-expand__media"
                src={`${src}#t=0.001`}
                muted
                playsInline
                preload="metadata"
                aria-label={alt}
              />
            ) : (
              <img ref={mediaRef} className="scroll-expand__media" src={src} alt={alt} />
            )}
            <div ref={scrimRef} className="scroll-expand__scrim" />
            {children && <div className="scroll-expand__overlay">{children}</div>}
          </div>
          {title && <div className="scroll-expand__title">{title}</div>}
          {scrollHint && (
            <div ref={hintRef} className="scroll-expand__hint" aria-hidden="true">
              <span className="scroll-expand__hint-label">{scrollHint}</span>
              <span className="scroll-expand__hint-icon" aria-hidden="true">
                <svg viewBox="0 0 24 20" fill="none"
                     stroke="currentColor"
                     strokeWidth={2}
                     strokeLinecap="round" strokeLinejoin="round">
                  <polyline points="4 3 12 10 20 3"/>
                  <polyline points="4 11 12 18 20 11"/>
                </svg>
              </span>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

window.ScrollExpand = ScrollExpand;
