/* BAZ26 — Hooks & utilities */

const { useState, useEffect, useRef, useCallback, useMemo, useLayoutEffect } = React;

// Reveal hook (IntersectionObserver)
function useReveal(opts = {}) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            setVisible(true);
            obs.disconnect();
          }
        });
      },
      { threshold: opts.threshold ?? 0.15, rootMargin: opts.rootMargin ?? '0px 0px -40px 0px' }
    );
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, []);
  return [ref, visible];
}

// Scramble hook — animates value to target text using random characters
function useScramble(target, trigger, opts = {}) {
  const chars = opts.chars || '!<>-_\\/[]{}—=+*^?#________';
  const duration = opts.duration ?? 900;
  const [out, setOut] = useState(target);
  useEffect(() => {
    if (!trigger) { setOut(target); return; }
    let raf;
    const start = performance.now();
    const fromText = '';
    const tick = (now) => {
      const t = Math.min((now - start) / duration, 1);
      let result = '';
      for (let i = 0; i < target.length; i++) {
        const reveal = (i / target.length);
        if (t > reveal + 0.2) {
          result += target[i];
        } else if (t > reveal) {
          result += chars[Math.floor(Math.random() * chars.length)];
        } else {
          result += target[i] === ' ' ? ' ' : (Math.random() < 0.6 ? chars[Math.floor(Math.random() * chars.length)] : ' ');
        }
      }
      setOut(result);
      if (t < 1) raf = requestAnimationFrame(tick);
      else setOut(target);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, trigger]);
  return out;
}

// Counter hook
function useCounter(target, trigger, duration = 1800) {
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!trigger) return;
    let raf;
    const start = performance.now();
    const tick = (now) => {
      const t = Math.min((now - start) / duration, 1);
      const eased = 1 - Math.pow(1 - t, 3);
      setVal(Math.round(target * eased));
      if (t < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, trigger]);
  return val;
}

// Magnetic button hook
function useMagnetic(strength = 0.35) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf;
    const onMove = (e) => {
      const rect = el.getBoundingClientRect();
      const x = e.clientX - (rect.left + rect.width / 2);
      const y = e.clientY - (rect.top + rect.height / 2);
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.transform = `translate(${x * strength}px, ${y * strength}px)`;
      });
    };
    const onLeave = () => {
      cancelAnimationFrame(raf);
      el.style.transform = 'translate(0,0)';
    };
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => {
      el.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
      cancelAnimationFrame(raf);
    };
  }, [strength]);
  return ref;
}

// Live time ticker
function useLiveTime(tz = 'Europe/Rome') {
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  return now.toLocaleTimeString('en-GB', {
    timeZone: tz,
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  });
}

// Custom cursor component
function CustomCursor() {
  const dotRef = useRef(null);
  const ringRef = useRef(null);
  useEffect(() => {
    // Honor reduced-motion + skip on touch / no-mouse devices.
    if (typeof window === 'undefined') return;
    const reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    const noHover = window.matchMedia && window.matchMedia('(hover: none)').matches;
    if (reduced || noHover) return;
    const dot = dotRef.current;
    const ring = ringRef.current;
    if (!dot || !ring) return;
    let mx = window.innerWidth / 2, my = window.innerHeight / 2;
    let rx = mx, ry = my;
    let raf;
    const onMove = (e) => {
      mx = e.clientX; my = e.clientY;
      dot.style.transform = `translate(${mx}px, ${my}px) translate(-50%,-50%)`;
    };
    const tick = () => {
      rx += (mx - rx) * 0.18;
      ry += (my - ry) * 0.18;
      ring.style.transform = `translate(${rx}px, ${ry}px) translate(-50%,-50%)`;
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);

    const onOver = (e) => {
      const t = e.target;
      if (!(t instanceof Element)) return;
      const interactive = t.closest('a, button, .magnetic, .value-row, .service-row, .venture, .lang-toggle button, .nav__link, [data-cursor]');
      const text = t.closest('input, textarea, [contenteditable]');
      if (text) {
        // On text inputs, fully hide the custom cursor so the native I-beam
        // is fully visible to the user (accessibility: must see caret).
        ring.classList.add('is-text');
        ring.classList.remove('is-hover');
        dot.classList.remove('is-hover');
        ring.style.opacity = '0';
        dot.style.opacity = '0';
      } else if (interactive) {
        ring.classList.add('is-hover');
        ring.classList.remove('is-text');
        dot.classList.add('is-hover');
        ring.style.opacity = '';
        dot.style.opacity = '';
      } else {
        ring.classList.remove('is-hover', 'is-text');
        dot.classList.remove('is-hover');
        ring.style.opacity = '';
        dot.style.opacity = '';
      }
    };
    document.addEventListener('mousemove', onMove);
    document.addEventListener('mouseover', onOver);
    return () => {
      document.removeEventListener('mousemove', onMove);
      document.removeEventListener('mouseover', onOver);
      cancelAnimationFrame(raf);
    };
  }, []);
  return (
    <>
      <div ref={ringRef} className="cursor-ring"></div>
      <div ref={dotRef} className="cursor-dot"></div>
    </>
  );
}

// Loader (shows only on the first visit per browser; uses localStorage)
function Loader() {
  const [done, setDone] = useState(false);
  const [pct, setPct] = useState(0);
  const [shouldShow] = useState(() => {
    // Skip loader entirely under prefers-reduced-motion (WCAG 2.3.3).
    try {
      if (typeof window !== 'undefined' && window.matchMedia &&
          window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
        return false;
      }
      return !localStorage.getItem('baz26_seen_loader');
    } catch (e) { return true; }
  });
  useEffect(() => {
    if (!shouldShow) { setDone(true); return; }
    const start = performance.now();
    const dur = 1500;
    let raf;
    const tick = (now) => {
      const t = Math.min((now - start) / dur, 1);
      setPct(Math.round(t * 100));
      if (t < 1) raf = requestAnimationFrame(tick);
      else setTimeout(() => {
        setDone(true);
        try { localStorage.setItem('baz26_seen_loader', '1'); } catch (e) {}
      }, 200);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [shouldShow]);
  if (!shouldShow) return null;
  return (
    <div className={`loader ${done ? 'is-done' : ''}`}>
      <div className="loader__brand">BAZ<em>26</em></div>
      <div className="loader__bar"></div>
      <div className="loader__pct mono">{String(pct).padStart(3, '0')} / 100</div>
    </div>
  );
}

// Reveal lines wrapper
function RevealLines({ children, className = '' }) {
  const [ref, visible] = useReveal();
  // children is an array of strings/elements per line
  const lines = Array.isArray(children) ? children : [children];
  return (
    <div ref={ref} className={`lines ${className} ${visible ? 'is-visible' : ''}`}>
      {lines.map((l, i) => (
        <span key={i} className="line"><span>{l}</span></span>
      ))}
    </div>
  );
}

Object.assign(window, {
  useReveal, useScramble, useCounter, useMagnetic, useLiveTime,
  CustomCursor, Loader, RevealLines,
});
