// Shared motion + utility primitives. Custom lightweight engine, in spirit of framer-motion.
const { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } = React;

/* ---------- In-view detection (scroll-based; works where IO is sandboxed) ---------- */
function useInView(ref, { threshold = 0.18, once = true } = {}) {
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    let stopped = false;
    const check = () => {
      const el = ref.current;
      if (!el || stopped) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight || 800;
      // visible when any part of the element is past 8% from bottom of viewport
      const visible = r.top < vh * 0.92 && r.bottom > vh * 0.08;
      if (visible) {
        setInView(true);
        if (once) { stopped = true; cleanup(); }
      } else if (!once) {
        setInView(false);
      }
    };
    const cleanup = () => {
      window.removeEventListener('scroll', check);
      window.removeEventListener('resize', check);
    };
    // initial check on next frame so layout has settled
    const raf = requestAnimationFrame(check);
    window.addEventListener('scroll', check, { passive: true });
    window.addEventListener('resize', check);
    return () => { stopped = true; cancelAnimationFrame(raf); cleanup(); };
  }, [ref, once]);
  return inView;
}

/* ---------- Reveal: fade + lift on enter view ---------- */
function Reveal({ as: Tag = 'div', delay = 0, y = 26, className = '', children, once = true, ...rest }) {
  const ref = useRef(null);
  const inView = useInView(ref, { once });
  const style = {
    transitionDelay: `${delay}ms`,
    transform: inView ? 'none' : `translate3d(0, ${y}px, 0)`,
    opacity: inView ? 1 : 0,
    transition: 'opacity 800ms cubic-bezier(.2,.7,.2,1), transform 900ms cubic-bezier(.2,.7,.2,1)',
    willChange: 'opacity, transform',
  };
  return <Tag ref={ref} style={style} className={className} {...rest}>{children}</Tag>;
}

/* ---------- Count-up number ---------- */
function useCountUp(end, { duration = 1600, startWhen = true, decimals = 0 } = {}) {
  const [val, setVal] = useState(0);
  const startedRef = useRef(false);
  useEffect(() => {
    if (!startWhen || startedRef.current) return;
    startedRef.current = true;
    const start = performance.now();
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(end * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [end, duration, startWhen]);
  return decimals > 0 ? val.toFixed(decimals) : Math.round(val);
}

function CountUp({ end, duration, decimals, format }) {
  const ref = useRef(null);
  const inView = useInView(ref);
  const v = useCountUp(end, { duration, startWhen: inView, decimals });
  const display = format ? format(v) : (decimals ? v : v.toLocaleString('tr-TR'));
  return <span ref={ref}>{display}</span>;
}

/* ---------- Live countdown timer ---------- */
function useCountdown(targetMs) {
  const [now, setNow] = useState(Date.now());
  useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, []);
  const diff = Math.max(0, targetMs - now);
  const d = Math.floor(diff / 86400000);
  const h = Math.floor((diff % 86400000) / 3600000);
  const m = Math.floor((diff % 3600000) / 60000);
  const s = Math.floor((diff % 60000) / 1000);
  return { d, h, m, s };
}

/* ---------- Typewriter ---------- */
function useTypewriter(lines, { speed = 22, lineDelay = 420, start = true } = {}) {
  const [out, setOut] = useState([]);
  const [done, setDone] = useState(false);
  useEffect(() => {
    if (!start) return;
    let cancelled = false;
    let acc = lines.map(() => '');
    setOut(acc.slice());
    (async () => {
      for (let i = 0; i < lines.length; i++) {
        const full = lines[i];
        for (let j = 1; j <= full.length; j++) {
          if (cancelled) return;
          acc[i] = full.slice(0, j);
          // jitter for character-by-character feel
          const ms = speed + (Math.random() * 18 - 9);
          await new Promise(r => setTimeout(r, ms));
          setOut(acc.slice());
        }
        await new Promise(r => setTimeout(r, lineDelay));
      }
      if (!cancelled) setDone(true);
    })();
    return () => { cancelled = true; };
  }, [start]); // eslint-disable-line
  return { out, done };
}

/* ---------- tiny class joiner ---------- */
const cx = (...xs) => xs.filter(Boolean).join(' ');

/* ---------- Pixel block (decorative) ---------- */
function PixelBlock({ size = 14, color = '#f5a623', className = '' }) {
  return (
    <span
      className={cx('inline-block', className)}
      style={{
        width: size, height: size, background: color,
        boxShadow: `inset -2px -2px 0 rgba(0,0,0,0.35), inset 2px 2px 0 rgba(255,255,255,0.18)`,
      }}
    />
  );
}

Object.assign(window, { useInView, Reveal, useCountUp, CountUp, useCountdown, useTypewriter, cx, PixelBlock });
