// hooks.jsx — shared hooks + helpers
const { useState, useEffect, useRef, useCallback } = React;

/* Reveal-on-scroll via IntersectionObserver */
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll('.reveal, .reveal-stagger');
    if (!('IntersectionObserver' in window)) {
      els.forEach(el => el.classList.add('is-visible'));
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add('is-visible');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.15, rootMargin: '0px 0px -60px 0px' });
    els.forEach(el => io.observe(el));
    return () => io.disconnect();
  }, []);
}

/* Parallax scroll (light; respects prefers-reduced-motion + intensity tweak) */
function useParallax(intensity) {
  useEffect(() => {
    if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    const mult = Math.max(0, intensity) / 100;
    if (mult === 0) return;

    const nodes = Array.from(document.querySelectorAll('[data-parallax]'));
    let raf;
    const update = () => {
      const vh = window.innerHeight;
      nodes.forEach(node => {
        const rate = parseFloat(node.dataset.parallax) || 0.2;
        const r = node.getBoundingClientRect();
        const mid = r.top + r.height / 2;
        const delta = (mid - vh / 2) * rate * mult * -1;
        node.style.transform = `translate3d(0, ${delta.toFixed(1)}px, 0) rotate(${(node.dataset.rotate || 0)}deg)`;
      });
      raf = null;
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); };
    update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll, { passive: true });
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [intensity]);
}

/* Count-up for metrics — not used by default but available */
function useCountUp(target, ref, trigger) {
  useEffect(() => {
    if (!trigger || !ref.current) return;
    const el = ref.current;
    const dur = 1400;
    const start = performance.now();
    const from = 0;
    const step = (now) => {
      const t = Math.min(1, (now - start) / dur);
      const eased = 1 - Math.pow(1 - t, 3);
      el.textContent = Math.round(from + (target - from) * eased);
      if (t < 1) requestAnimationFrame(step);
    };
    requestAnimationFrame(step);
  }, [trigger, target]);
}

function topoRingPaths({ cx, cy, r0, count, growth, seed, samples = 140 }) {
  let s = seed;
  const rand = () => (s = (s * 16807) % 2147483647) / 2147483647;
  const rings = [];
  let r = r0;
  for (let i = 0; i < count; i++) {
    const phi = rand() * Math.PI * 2, psi = rand() * Math.PI * 2;
    const pts = [];
    for (let k = 0; k < samples; k++) {
      const th = (k / samples) * Math.PI * 2;
      const rr = r * (1 + 0.06 * Math.sin(3 * th + phi) + 0.035 * Math.sin(7 * th + psi));
      pts.push([cx + rr * Math.cos(th), cy + rr * Math.sin(th)]);
    }
    let d = `M ${pts[0][0].toFixed(1)} ${pts[0][1].toFixed(1)}`;
    for (let k = 0; k < pts.length; k++) {
      const p0 = pts[(k - 1 + pts.length) % pts.length];
      const p1 = pts[k];
      const p2 = pts[(k + 1) % pts.length];
      const p3 = pts[(k + 2) % pts.length];
      const c1 = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6];
      const c2 = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6];
      d += ` C ${c1[0].toFixed(1)} ${c1[1].toFixed(1)}, ${c2[0].toFixed(1)} ${c2[1].toFixed(1)}, ${p2[0].toFixed(1)} ${p2[1].toFixed(1)}`;
    }
    rings.push({ d: d + ' Z', i });
    r *= growth;
  }
  return rings;
}

Object.assign(window, { useReveal, useParallax, useCountUp, topoRingPaths });
