// ── CIRA Labs — Interaction utilities ─────────────────────────────────

// ── Custom cursor ──────────────────────────────────────────────────────
function CustomCursor() {
  const dotRef = useRef(null);
  const ringRef = useRef(null);
  const pos = useRef({ x: -200, y: -200 });
  const ringPos = useRef({ x: -200, y: -200 });
  const hovered = useRef(false);
  const clicking = useRef(false);

  useEffect(() => {
    if (window.matchMedia('(pointer: coarse)').matches) return;
    const onMove = (e) => { pos.current = { x: e.clientX, y: e.clientY }; };
    const onOver = (e) => { hovered.current = !!e.target.closest('a,button,[role="button"]'); };
    const onDown = () => { clicking.current = true; };
    const onUp = () => { clicking.current = false; };
    window.addEventListener('mousemove', onMove, { passive: true });
    window.addEventListener('mouseover', onOver, { passive: true });
    window.addEventListener('mousedown', onDown);
    window.addEventListener('mouseup', onUp);

    let raf;
    const tick = () => {
      const { x, y } = pos.current;
      if (dotRef.current) {
        dotRef.current.style.transform = `translate(${x - 3}px,${y - 3}px) scale(${clicking.current ? 0.4 : 1})`;
      }
      ringPos.current.x += (x - ringPos.current.x) * 0.11;
      ringPos.current.y += (y - ringPos.current.y) * 0.11;
      if (ringRef.current) {
        const scale = hovered.current ? 1.8 : clicking.current ? 0.75 : 1;
        ringRef.current.style.transform = `translate(${ringPos.current.x - 16}px,${ringPos.current.y - 16}px) scale(${scale})`;
        ringRef.current.style.borderColor = hovered.current ? 'var(--accent-from)' : 'rgba(180,195,255,0.3)';
        ringRef.current.style.opacity = hovered.current ? '0.9' : '0.5';
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);

    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseover', onOver);
      window.removeEventListener('mousedown', onDown);
      window.removeEventListener('mouseup', onUp);
      cancelAnimationFrame(raf);
    };
  }, []);

  if (window.matchMedia('(pointer: coarse)').matches) return null;
  return (
    <>
      <div ref={dotRef} aria-hidden="true" style={{
        position: 'fixed', top: 0, left: 0, zIndex: 9999, pointerEvents: 'none',
        width: 6, height: 6, borderRadius: '50%',
        background: 'var(--accent-from)',
        transition: 'transform 0.06s',
        mixBlendMode: 'screen',
      }} />
      <div ref={ringRef} aria-hidden="true" style={{
        position: 'fixed', top: 0, left: 0, zIndex: 9998, pointerEvents: 'none',
        width: 32, height: 32, borderRadius: '50%',
        border: '1px solid rgba(180,195,255,0.3)',
        transition: 'border-color 0.25s, opacity 0.25s, transform 0.12s',
      }} />
    </>
  );
}

// ── ScrollProgress ─────────────────────────────────────────────────────
function ScrollProgress() {
  const [pct, setPct] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const el = document.documentElement;
      const scrolled = el.scrollTop || document.body.scrollTop;
      const total = el.scrollHeight - el.clientHeight;
      setPct(total > 0 ? (scrolled / total) * 100 : 0);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return (
    <div aria-hidden="true" style={{
      position: 'fixed', top: 0, left: 0, zIndex: 9997, height: 2,
      width: `${pct}%`,
      background: 'linear-gradient(to right, var(--accent-from), var(--accent-to))',
      transition: 'width 0.08s linear',
      pointerEvents: 'none',
      boxShadow: '0 0 10px var(--accent-from)',
    }} />
  );
}

// ── useTilt ────────────────────────────────────────────────────────────
function useTilt(strength = 7) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el || window.matchMedia('(pointer: coarse)').matches) return;
    const onEnter = () => { el.style.transition = 'none'; };
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const x = ((e.clientX - r.left) / r.width - 0.5) * strength;
      const y = ((e.clientY - r.top) / r.height - 0.5) * strength;
      el.style.transform = `perspective(700px) rotateY(${x}deg) rotateX(${-y}deg) translateY(-4px)`;
    };
    const onLeave = () => {
      el.style.transition = 'transform 0.65s cubic-bezier(.16,1,.3,1)';
      el.style.transform = '';
    };
    el.addEventListener('mouseenter', onEnter);
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => {
      el.removeEventListener('mouseenter', onEnter);
      el.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
    };
  }, [strength]);
  return ref;
}

// ── CountUp ────────────────────────────────────────────────────────────
function CountUp({ value, duration = 1600 }) {
  const [display, setDisplay] = useState(value);
  const triggered = useRef(false);
  const ref = useRef(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(([entry]) => {
      if (!entry.isIntersecting || triggered.current) return;
      triggered.current = true;
      io.disconnect();

      const str = String(value);
      const m = str.match(/^([0-9,\.]+)(.*)/);
      if (!m) return;
      const raw = parseFloat(m[1].replace(/,/g, ''));
      const suffix = m[2];
      const hasComma = m[1].includes(',');
      const decimals = (m[1].split('.')[1] || '').length;
      const padLen = m[1].length;
      const start = performance.now();

      const tick = (now) => {
        const p = Math.min((now - start) / duration, 1);
        const eased = 1 - Math.pow(1 - p, 3);
        const cur = raw * eased;
        let formatted;
        if (hasComma) formatted = Math.round(cur).toLocaleString() + suffix;
        else if (decimals > 0) formatted = cur.toFixed(decimals) + suffix;
        else formatted = String(Math.round(cur)).padStart(padLen, '0') + suffix;
        setDisplay(formatted);
        if (p < 1) requestAnimationFrame(tick);
        else setDisplay(value);
      };
      requestAnimationFrame(tick);
    }, { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [value, duration]);

  return <span ref={ref}>{display}</span>;
}

// ── useScramble ────────────────────────────────────────────────────────
const SCRAMBLE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789·∘⊕';
function useScramble(text, delay = 0) {
  const [out, setOut] = useState('');
  useEffect(() => {
    let frame = 0;
    let raf;
    const len = text.length;
    const delayFrames = Math.round(delay / 16);
    const tick = () => {
      frame++;
      if (frame < delayFrames) { raf = requestAnimationFrame(tick); return; }
      const elapsed = frame - delayFrames;
      const resolved = Math.floor(elapsed / 2.5);
      const result = text.split('').map((ch, i) => {
        if (ch === ' ' || ch === '—' || i < resolved) return ch;
        return SCRAMBLE_CHARS[Math.floor(Math.random() * SCRAMBLE_CHARS.length)];
      }).join('');
      setOut(result);
      if (resolved < len) raf = requestAnimationFrame(tick);
      else setOut(text);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [text, delay]);
  return out;
}

// ── useMagnetic ────────────────────────────────────────────────────────
function useMagnetic(strength = 0.35) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el || window.matchMedia('(pointer: coarse)').matches) return;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
      const dx = e.clientX - cx, dy = e.clientY - cy;
      const dist = Math.hypot(dx, dy);
      const threshold = Math.max(r.width, r.height) * 1.4;
      if (dist < threshold) {
        const force = strength * (1 - dist / threshold);
        el.style.transition = 'none';
        el.style.transform = `translate(${dx * force}px,${dy * force}px)`;
      }
    };
    const onLeave = () => {
      el.style.transition = 'transform 0.55s cubic-bezier(.16,1,.3,1)';
      el.style.transform = '';
    };
    window.addEventListener('mousemove', onMove, { passive: true });
    el.addEventListener('mouseleave', onLeave);
    return () => {
      window.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
    };
  }, [strength]);
  return ref;
}

Object.assign(window, { CustomCursor, ScrollProgress, useTilt, CountUp, useScramble, useMagnetic });
