/* global React */
const { useState, useEffect, useRef, useLayoutEffect } = React;

/* ── Custom cursor ──────────────────────────────── */
function Cursor() {
  const dot = useRef(null);
  const ring = useRef(null);
  const [hover, setHover] = useState(null); // null | 'view' | 'drag' | 'click'
  const [click, setClick] = useState(false);

  useEffect(() => {
    let dx=0,dy=0,rx=0,ry=0,mx=0,my=0,raf;
    const onMove = e => { mx=e.clientX; my=e.clientY; };
    const onDown = () => setClick(true);
    const onUp = () => setClick(false);
    const tick = () => {
      dx += (mx-dx)*0.55; dy += (my-dy)*0.55;
      rx += (mx-rx)*0.14; ry += (my-ry)*0.14;
      if (dot.current) { dot.current.style.left=dx+'px'; dot.current.style.top=dy+'px'; }
      if (ring.current) { ring.current.style.left=rx+'px'; ring.current.style.top=ry+'px'; }
      raf = requestAnimationFrame(tick);
    };
    document.addEventListener('mousemove', onMove);
    document.addEventListener('mousedown', onDown);
    document.addEventListener('mouseup', onUp);
    raf = requestAnimationFrame(tick);

    const bind = () => {
      document.querySelectorAll('[data-cursor]').forEach(el => {
        if (el.__bound) return;
        el.__bound = true;
        const label = el.getAttribute('data-cursor');
        el.addEventListener('mouseenter', () => setHover(label));
        el.addEventListener('mouseleave', () => setHover(null));
      });
      document.querySelectorAll('a, button').forEach(el => {
        if (el.__boundLink || el.hasAttribute('data-cursor')) return;
        el.__boundLink = true;
        el.addEventListener('mouseenter', () => setHover('click'));
        el.addEventListener('mouseleave', () => setHover(null));
      });
    };
    bind();
    const obs = new MutationObserver(bind);
    obs.observe(document.body, { childList: true, subtree: true });

    return () => {
      document.removeEventListener('mousemove', onMove);
      document.removeEventListener('mousedown', onDown);
      document.removeEventListener('mouseup', onUp);
      cancelAnimationFrame(raf);
      obs.disconnect();
    };
  }, []);

  const ringLabel = hover === 'view' ? 'View' : hover === 'drag' ? 'Drag' : '';
  return (
    <>
      <div ref={dot} className={`cursor-dot ${hover ? 'is-hovering':''}`} />
      <div ref={ring} className={`cursor-ring ${hover ? 'is-hovering':''} ${click?'is-clicking':''}`}>
        {ringLabel}
      </div>
    </>
  );
}

/* ── Word reveal (uses IntersectionObserver) ────── */
function Reveal({ text, as='span', delayStep=0.06, baseDelay=0, className='' }) {
  const ref = useRef(null);
  const [vis, setVis] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => { if (e.isIntersecting) { setVis(true); io.disconnect(); } });
    }, { threshold: 0.2 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  const Tag = as;
  const words = String(text).split(' ');
  return (
    <Tag ref={ref} className={className} aria-label={text}>
      {words.map((w, i) => (
        <span key={i} className={`word-mask ${vis?'is-visible':''}`} style={{marginRight: '0.25em'}}>
          <span style={{ transitionDelay: `${baseDelay + i*delayStep}s` }}>{w}</span>
        </span>
      ))}
    </Tag>
  );
}

/* ── Generic fade-up wrapper ─────────────────────── */
function FadeUp({ children, delay=0, className='', as='div' }) {
  const ref = useRef(null);
  const [vis, setVis] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => { if (e.isIntersecting) { setVis(true); io.disconnect(); } });
    }, { threshold: 0.15 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  const Tag = as;
  return (
    <Tag ref={ref} className={`fade-up ${vis?'is-visible':''} ${className}`} style={{ transitionDelay: `${delay}s` }}>
      {children}
    </Tag>
  );
}

/* ── Magnetic wrapper ───────────────────────────── */
function Magnetic({ children, strength=0.35 }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let x=0,y=0,tx=0,ty=0,raf;
    const move = e => {
      const r = el.getBoundingClientRect();
      tx = (e.clientX - r.left - r.width/2) * strength;
      ty = (e.clientY - r.top - r.height/2) * strength;
    };
    const leave = () => { tx=0; ty=0; };
    const tick = () => {
      x += (tx-x)*0.18; y += (ty-y)*0.18;
      el.style.transform = `translate(${x}px, ${y}px)`;
      raf = requestAnimationFrame(tick);
    };
    el.addEventListener('mousemove', move);
    el.addEventListener('mouseleave', leave);
    raf = requestAnimationFrame(tick);
    return () => {
      el.removeEventListener('mousemove', move);
      el.removeEventListener('mouseleave', leave);
      cancelAnimationFrame(raf);
    };
  }, [strength]);
  return <div ref={ref} style={{ display: 'inline-block' }}>{children}</div>;
}

/* ── Loader ─────────────────────────────────────── */
function Loader({ onDone }) {
  const [n, setN] = useState(0);
  const [leaving, setLeaving] = useState(false);
  useEffect(() => {
    let v = 0;
    const step = () => {
      v += Math.floor(Math.random()*9) + 3;
      if (v >= 100) {
        setN(100);
        setTimeout(() => { setLeaving(true); setTimeout(onDone, 900); }, 300);
      } else {
        setN(v);
        setTimeout(step, 30 + Math.random()*40);
      }
    };
    setTimeout(step, 80);
  }, [onDone]);
  return (
    <div className={`loader ${leaving?'is-leaving':''}`}>
      <div style={{ textAlign: 'center' }}>
        <div className="serif" style={{ fontSize: 'clamp(72px, 12vw, 180px)', lineHeight: 0.9, fontFeatureSettings: '"tnum"' }}>
          {String(n).padStart(2,'0')}
        </div>
        <div className="mono" style={{ fontSize: 11, letterSpacing: '0.2em', textTransform: 'uppercase', marginTop: 18, color: 'var(--ink-mid)' }}>
          Aura — Loading Spaces
        </div>
      </div>
    </div>
  );
}

window.Cursor = Cursor;
window.Reveal = Reveal;
window.FadeUp = FadeUp;
window.Magnetic = Magnetic;
window.Loader = Loader;
