/* ============================================================
   MP&P — motion primitives (GSAP + ScrollTrigger)
   exports: Cursor, Grain, ScrollBar, Preloader, SplitText,
            ClipImg, Magnetic, Counter, Spinner, Marquee, HPin
   ============================================================ */
const G = window.gsap || null;
const ST = window.ScrollTrigger || null;
if (G && ST) G.registerPlugin(ST);
const COARSE = window.matchMedia('(pointer:coarse)').matches;
const RM = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

/* ---------- custom cursor ---------- */
function Cursor() {
  useEffect(() => {
    if (COARSE) return;
    const html = document.documentElement;
    html.classList.add('has-cur');
    const wrap = document.createElement('div');
    wrap.className = 'cur';
    wrap.innerHTML = '<div class="cur__ring"><span class="lbl">Ver</span></div><div class="cur__dot"></div>';
    document.body.appendChild(wrap);
    const ring = wrap.querySelector('.cur__ring');
    const dot = wrap.querySelector('.cur__dot');
    const lbl = wrap.querySelector('.lbl');
    let mx = innerWidth / 2, my = innerHeight / 2, rx = mx, ry = my, dx = mx, dy = my, pressed = false;
    const move = (e) => { mx = e.clientX; my = e.clientY; };
    const over = (e) => {
      const t = e.target.closest && e.target.closest('[data-cur],a,button,input,select,textarea,label');
      html.classList.remove('cur-link', 'cur-view');
      if (!t) return;
      const k = t.getAttribute && t.getAttribute('data-cur');
      if (k === 'view') { html.classList.add('cur-view'); lbl.textContent = t.getAttribute('data-cur-label') || 'Ver'; }
      else { html.classList.add('cur-link'); }
    };
    const down = () => { pressed = true; };
    const up = () => { pressed = false; };
    window.addEventListener('mousemove', move);
    document.addEventListener('mouseover', over);
    window.addEventListener('mousedown', down);
    window.addEventListener('mouseup', up);
    let raf;
    const loop = () => {
      rx += (mx - rx) * 0.16; ry += (my - ry) * 0.16;
      dx += (mx - dx) * 0.42; dy += (my - dy) * 0.42;
      ring.style.transform = `translate(${rx}px,${ry}px) scale(${pressed ? 0.82 : 1})`;
      dot.style.transform = `translate(${dx}px,${dy}px)`;
      raf = requestAnimationFrame(loop);
    };
    loop();
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('mousemove', move);
      document.removeEventListener('mouseover', over);
      window.removeEventListener('mousedown', down);
      window.removeEventListener('mouseup', up);
      wrap.remove();
      html.classList.remove('has-cur', 'cur-link', 'cur-view');
    };
  }, []);
  return null;
}

function Grain() { return <div className="grain" aria-hidden="true" />; }

function ScrollBar() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    const on = () => {
      const h = document.documentElement;
      const p = h.scrollTop / ((h.scrollHeight - h.clientHeight) || 1);
      el.style.transform = `scaleX(${p})`;
    };
    window.addEventListener('scroll', on, { passive: true });
    on();
    return () => window.removeEventListener('scroll', on);
  }, []);
  return <div className="scrollbar" ref={ref} aria-hidden="true" />;
}

/* ---------- preloader ---------- */
function Preloader({ onDone }) {
  const [n, setN] = useState(0);
  const [exit, setExit] = useState(false);
  useEffect(() => {
    if (RM) { setExit(true); const t = setTimeout(() => onDone && onDone(), 300); return () => clearTimeout(t); }
    const dur = 1500, t0 = performance.now();
    let raf;
    const tick = (t) => {
      const p = Math.min((t - t0) / dur, 1);
      const eased = 1 - Math.pow(1 - p, 3);
      setN(Math.round(eased * 100));
      if (p < 1) raf = requestAnimationFrame(tick);
      else { setExit(true); setTimeout(() => onDone && onDone(), 850); }
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);
  return (
    <div className="preload" style={exit ? { transform: 'translateY(-100%)', transition: 'transform .85s var(--ease-expo)' } : null} aria-hidden="true">
      <div className="preload__brand">MP&amp;P — METAL PROFILES &amp; PLASTICS</div>
      <div className="preload__row">
        <div className="preload__num">{String(n).padStart(2, '0')}</div>
        <div className="preload__lbl">Loading material · {n}%</div>
      </div>
      <div className="preload__track" style={{ width: n + '%' }} />
    </div>
  );
}

/* ---------- split text reveal (CSS-driven via IO) ---------- */
function SplitText({ text, accent, as: Tag = 'h2', className, style, stagger = 0.05 }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (RM) { el.classList.add('in'); return; }
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => { if (e.isIntersecting) { el.classList.add('in'); io.unobserve(el); } });
    }, { threshold: 0.15, rootMargin: '0px 0px -6% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, [text]);
  const words = String(text).split(' ');
  return (
    <Tag ref={ref} className={'split ' + (className || '')} style={style}>
      {words.map((w, i) => {
        const clean = w.toLowerCase().replace(/[^a-z&]/g, '');
        const isAcc = accent && clean.includes(accent.toLowerCase());
        return (
          <React.Fragment key={i}>
            <span className="word-mask"><span className={'word-in' + (isAcc ? ' accentword' : '')} style={{ transitionDelay: (i * stagger) + 's' }}>{w}</span></span>
            {i < words.length - 1 ? ' ' : ''}
          </React.Fragment>
        );
      })}
    </Tag>
  );
}

/* ---------- clip-reveal image (CSS-driven via IO) ---------- */
function ClipImg({ src, alt, label, tag, eager, view, className, style }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (RM) { el.classList.add('in'); return; }
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => { if (e.isIntersecting) { el.classList.add('in'); io.unobserve(el); } });
    }, { threshold: 0.12, rootMargin: '0px 0px -4% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <div className={'frame creveal ' + (className || '')} ref={ref} style={style}
         data-cur={view ? 'view' : undefined} data-cur-label={view ? (typeof view === 'string' ? view : 'Ver') : undefined}>
      <Img src={src} alt={alt} label={label} eager={eager} />
      {tag && <span className="tag">{tag}</span>}
    </div>
  );
}

/* ---------- magnetic ---------- */
function Magnetic({ children, strength = 0.3, className }) {
  const ref = useRef(null);
  useEffect(() => {
    if (COARSE) return;
    const el = ref.current;
    let r = null;
    const enter = () => { r = el.getBoundingClientRect(); };
    const move = (e) => {
      if (!r) r = el.getBoundingClientRect();
      const x = (e.clientX - (r.left + r.width / 2)) * strength;
      const y = (e.clientY - (r.top + r.height / 2)) * strength;
      if (G) G.to(el, { x, y, duration: 0.6, ease: 'expo.out' }); else el.style.transform = `translate(${x}px,${y}px)`;
    };
    const leave = () => { if (G) G.to(el, { x: 0, y: 0, duration: 0.7, ease: 'elastic.out(1,0.4)' }); else el.style.transform = ''; };
    el.addEventListener('mouseenter', enter);
    el.addEventListener('mousemove', move);
    el.addEventListener('mouseleave', leave);
    return () => { el.removeEventListener('mouseenter', enter); el.removeEventListener('mousemove', move); el.removeEventListener('mouseleave', leave); };
  }, [strength]);
  return <span className={'mag ' + (className || '')} ref={ref}>{children}</span>;
}

/* ---------- counter (IO + rAF) ---------- */
function Counter({ value, className }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    const m = String(value).match(/^(\D*)(\d+(?:\.\d+)?)(.*)$/);
    if (!m) { el.textContent = value; return; }
    const pre = m[1], num = parseFloat(m[2]), suf = m[3];
    el.textContent = pre + '0' + suf;
    if (RM) { el.textContent = pre + num + suf; return; }
    let done = false;
    const run = () => {
      const dur = 1500, t0 = performance.now();
      const tick = (t) => {
        const p = Math.min((t - t0) / dur, 1);
        const e = 1 - Math.pow(1 - p, 3);
        el.textContent = pre + Math.round(e * num) + suf;
        if (p < 1) requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick);
    };
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => { if (e.isIntersecting && !done) { done = true; run(); io.unobserve(el); } });
    }, { threshold: 0.5 });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return <span ref={ref} className={className}>0</span>;
}

/* ---------- spinning circular badge ---------- */
function Spinner({ text = 'Precision Fabrication · Since 1992 · ', onClick }) {
  return (
    <div className="spinner" data-cur="link" onClick={onClick}>
      <svg viewBox="0 0 100 100" aria-hidden="true">
        <defs><path id="cTxt" d="M50,50 m-37,0 a37,37 0 1,1 74,0 a37,37 0 1,1 -74,0" /></defs>
        <text><textPath href="#cTxt" startOffset="0">{text + text}</textPath></text>
      </svg>
      <span className="spinner__arrow" aria-hidden="true">↓</span>
    </div>
  );
}

/* ---------- marquee ---------- */
function Marquee({ items, dur = 28, reverse, dark, line, sep = '·' }) {
  const seq = items.concat(items);
  return (
    <div className={'marquee' + (reverse ? ' marquee--rev' : '') + (dark ? ' marquee--dark' : '') + (line ? ' marquee--line' : '')}>
      <div className="marquee__track" style={{ '--marq-dur': dur + 's' }}>
        {seq.map((it, i) => (
          <span className="marquee__item" key={i}>{it}<span className="dot" /></span>
        ))}
      </div>
    </div>
  );
}

/* ---------- horizontal pinned gallery ---------- */
function HPin({ eyebrow, title, items, go }) {
  const sec = useRef(null), track = useRef(null), prog = useRef(null);
  useEffect(() => {
    if (!G || !ST || RM) return;
    if (window.matchMedia('(max-width:920px)').matches) return;
    const ctx = G.context(() => {
      const t = track.current;
      const total = () => Math.max(0, t.scrollWidth - window.innerWidth + 72);
      G.to(t, {
        x: () => -total(), ease: 'none',
        scrollTrigger: {
          trigger: sec.current, start: 'top top', end: () => '+=' + total(),
          scrub: 0.6, pin: true, anticipatePin: 1, invalidateOnRefresh: true,
          onUpdate: (s) => { if (prog.current) prog.current.style.width = (s.progress * 100) + '%'; },
        },
      });
    }, sec);
    return () => ctx.revert();
  }, [items]);
  return (
    <section className="hpin" ref={sec}>
      <div className="hpin__sticky">
        <div className="hpin__head">
          <div>
            <Eyebrow accent>{eyebrow}</Eyebrow>
            <h2 className="h2" style={{ marginTop: 14 }}>{title}</h2>
          </div>
          <span className="mono" style={{ fontSize: 12, color: 'var(--on-steel-2)' }}>Drag · Scroll →</span>
        </div>
        <div className="hpin__track" ref={track}>
          {items.map((it, i) => (
            <a className="hcard" key={i} href={it.href || '#'} data-cur="view" data-cur-label="Ver"
               onClick={(e) => { if (it.onClick) { e.preventDefault(); it.onClick(); } }}>
              <div className="hcard__media">
                <span className="hcard__n">{String(i + 1).padStart(2, '0')}</span>
                <Img src={it.img} alt={it.name} label={it.name} />
              </div>
              <div className="hcard__body"><h4>{it.name}</h4><span>{it.meta}</span></div>
            </a>
          ))}
        </div>
        <div className="hpin__progress"><i ref={prog} /></div>
      </div>
    </section>
  );
}

Object.assign(window, { Cursor, Grain, ScrollBar, Preloader, SplitText, ClipImg, Magnetic, Counter, Spinner, Marquee, HPin, MPP_G: G, MPP_ST: ST });
