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

/* ---- Reduced-motion shortcut ---- */
const prefersReduced = () =>
  typeof window !== 'undefined' &&
  window.matchMedia &&
  window.matchMedia('(prefers-reduced-motion: reduce)').matches;

/* ---- Scroll-reveal: adds .visible when element intersects viewport ---- */
function useReveal(opts = {}) {
  const ref = useRef(null);
  const { threshold = 0.15, rootMargin = '0px 0px -60px 0px' } = opts;
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (prefersReduced()) { el.classList.add('visible'); return; }
    // Above-the-fold elements reveal synchronously to avoid FOIC.
    const rect = el.getBoundingClientRect();
    if (rect.top < window.innerHeight && rect.bottom > 0) {
      el.classList.add('visible');
      return;
    }
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            e.target.classList.add('visible');
            io.unobserve(e.target);
          }
        });
      },
      { threshold, rootMargin }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return ref;
}

/* ---- Mask-reveal (clip-path wipe) — same trigger as useReveal but
       cooperates with .mask-reveal CSS to do a clip-path 100%→0 sweep. ---- */
function useMaskReveal(direction = 'left') {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.dataset.maskDir = direction;
    if (prefersReduced()) { el.classList.add('visible'); return; }
    const rect = el.getBoundingClientRect();
    if (rect.top < window.innerHeight * 0.85 && rect.bottom > 0) {
      el.classList.add('visible'); return;
    }
    const io = new IntersectionObserver(
      (entries) => entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('visible');
          io.unobserve(e.target);
        }
      }),
      { threshold: 0.2 }
    );
    io.observe(el);
    return () => io.disconnect();
  }, [direction]);
  return ref;
}

/* ---- Animated counter: tweens 0 → target when in view ---- */
function useCounter(target, duration = 1400) {
  const [val, setVal] = useState(0);
  const ref = useRef(null);
  const started = useRef(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (prefersReduced()) { setVal(target); return; }
    const start = () => {
      if (started.current) return;
      started.current = true;
      const t0 = performance.now();
      const tick = (now) => {
        const t = Math.min(1, (now - t0) / duration);
        const eased = 1 - Math.pow(1 - t, 3); // easeOutCubic
        setVal(Math.round(eased * target));
        if (t < 1) requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick);
    };
    // Already on-screen? start now.
    const rect = el.getBoundingClientRect();
    if (rect.top < window.innerHeight && rect.bottom > 0) {
      start();
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) { start(); io.unobserve(el); } });
    }, { threshold: 0.3 });
    io.observe(el);
    return () => io.disconnect();
  }, [target, duration]);
  return [val, ref];
}

/* ---- Countdown to a target ISO date string ---- */
function useCountdown(targetIso) {
  const [now, setNow] = useState(() => Date.now());
  useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, []);
  const target = new Date(targetIso).getTime();
  let diff = Math.max(0, target - now);
  const days = Math.floor(diff / 86400000); diff -= days * 86400000;
  const hours = Math.floor(diff / 3600000); diff -= hours * 3600000;
  const minutes = Math.floor(diff / 60000); diff -= minutes * 60000;
  const seconds = Math.floor(diff / 1000);
  return { days, hours, minutes, seconds };
}

/* ---- Magnetic CTA: subtle pull toward cursor on mousemove ---- */
function useMagnet({ strength = 0.35, radius = 140 } = {}) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (prefersReduced() || matchMedia('(pointer: coarse)').matches) return;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width / 2;
      const cy = r.top + r.height / 2;
      const dx = e.clientX - cx;
      const dy = e.clientY - cy;
      const dist = Math.hypot(dx, dy);
      if (dist > radius) {
        el.style.transform = '';
        return;
      }
      el.style.transform = `translate(${dx * strength}px, ${dy * strength}px)`;
    };
    const onLeave = () => { el.style.transform = ''; };
    window.addEventListener('mousemove', onMove, { passive: true });
    el.addEventListener('mouseleave', onLeave);
    return () => {
      window.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
    };
  }, [strength, radius]);
  return ref;
}

/* ---- Parallax-Y: translateY based on scroll offset (subtle) ---- */
function useParallaxY(speed = -0.15) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el || prefersReduced()) return;
    let rafId = 0;
    const tick = () => {
      const r = el.getBoundingClientRect();
      const center = r.top + r.height / 2;
      const delta = (center - window.innerHeight / 2) * speed;
      el.style.transform = `translate3d(0, ${delta.toFixed(1)}px, 0)`;
      rafId = 0;
    };
    const onScroll = () => { if (!rafId) rafId = requestAnimationFrame(tick); };
    tick();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => {
      window.removeEventListener('scroll', onScroll);
      if (rafId) cancelAnimationFrame(rafId);
    };
  }, [speed]);
  return ref;
}

/* ---- Tilt: small rotateX/Y on hover, native CSS-only otherwise ---- */
function useTilt({ max = 6 } = {}) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (prefersReduced() || matchMedia('(pointer: coarse)').matches) return;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width / 2;
      const cy = r.top + r.height / 2;
      const px = (e.clientX - cx) / (r.width / 2);
      const py = (e.clientY - cy) / (r.height / 2);
      el.style.transform = `perspective(900px) rotateY(${px * max}deg) rotateX(${-py * max}deg) translateZ(0)`;
    };
    const onLeave = () => { el.style.transform = ''; };
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => {
      el.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
    };
  }, [max]);
  return ref;
}

/* ---- Format integers (FR thin space / EN comma) ---- */
function fmtInt(n, lang) {
  const s = String(Math.round(n));
  if (lang === 'fr') return s.replace(/\B(?=(\d{3})+(?!\d))/g, '\u202F');
  return s.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

window.useReveal = useReveal;
window.useMaskReveal = useMaskReveal;
window.useCounter = useCounter;
window.useCountdown = useCountdown;
window.useMagnet = useMagnet;
window.useTilt = useTilt;
window.useParallaxY = useParallaxY;
window.fmtInt = fmtInt;
