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

/* ---- reveal-on-scroll wrapper ---- */
function Reveal({ children, delay = 0, as = "div", className = "", style = {} }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setSeen(true); io.disconnect(); }
    }, { threshold: 0.15, rootMargin: "0px 0px -8% 0px" });
    io.observe(el); return () => io.disconnect();
  }, []);
  const Tag = as;
  return (
    <Tag ref={ref} className={`reveal ${seen ? "in" : ""} ${className}`}
      style={{ ...style, transitionDelay: seen ? `${delay}ms` : "0ms" }}>
      {children}
    </Tag>
  );
}

/* ---- count-up number, triggers on view ---- */
function useInView(threshold = 0.4) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = ref.current;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setInView(true); io.disconnect(); }
    }, { threshold });
    io.observe(el); return () => io.disconnect();
  }, [threshold]);
  return [ref, inView];
}

function Counter({ value, prefix = "", suffix = "", decimals = 0, dur = 1500 }) {
  const [ref, inView] = useInView(0.5);
  const [n, setN] = useState(0);
  useEffect(() => {
    if (!inView) return;
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) { setN(value); return; }
    let raf, start;
    const step = (t) => {
      if (!start) start = t;
      const p = Math.min((t - start) / dur, 1);
      const eased = 1 - Math.pow(1 - p, 3);
      setN(value * eased);
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [inView, value, dur]);
  const display = decimals ? n.toFixed(decimals) : Math.round(n).toLocaleString();
  return (
    <span ref={ref} className="num">
      {prefix}{display}<span className="suf">{suffix}</span>
    </span>
  );
}

/* ---- animated horizontal bar (grows on view) ---- */
function BarChart({ caption, bars }) {
  const [ref, inView] = useInView(0.4);
  const max = Math.max(...bars.map(b => b.pct));
  return (
    <div className="chart" ref={ref}>
      {caption && <div className="chart-cap">{caption}</div>}
      {bars.map((b, i) => (
        <div className="bar-row" key={i}>
          <div className="bar-top">
            <span className="bar-label">{b.label}</span>
            <span className="bar-val">{b.value}</span>
          </div>
          <div className="bar-track">
            <div className="bar-fill" style={{
              transform: inView ? `scaleX(${b.pct / max})` : 'scaleX(0)',
              transitionDelay: `${i * 60}ms`
            }} />
          </div>
        </div>
      ))}
    </div>
  );
}

/* ---- magnetic button (cursor-following with lerp) ---- */
function Magnetic({ children, className = "", strength = 0.4, onClick, style = {} }) {
  const ref = useRef(null);
  const fine = typeof window !== "undefined" && window.matchMedia("(hover: hover) and (pointer: fine)").matches;
  const target = useRef({ x: 0, y: 0 });
  const current = useRef({ x: 0, y: 0 });
  const rafId = useRef(null);

  const animate = useCallback(() => {
    const el = ref.current;
    if (!el) return;
    current.current.x += (target.current.x - current.current.x) * 0.12;
    current.current.y += (target.current.y - current.current.y) * 0.12;
    el.style.transform = `translate(${current.current.x}px, ${current.current.y}px)`;
    if (Math.abs(target.current.x - current.current.x) > 0.01 || Math.abs(target.current.y - current.current.y) > 0.01) {
      rafId.current = requestAnimationFrame(animate);
    } else {
      rafId.current = null;
    }
  }, []);

  const move = (e) => {
    if (!fine) return;
    const el = ref.current; const r = el.getBoundingClientRect();
    target.current.x = (e.clientX - (r.left + r.width / 2)) * strength;
    target.current.y = (e.clientY - (r.top + r.height / 2)) * strength;
    if (!rafId.current) rafId.current = requestAnimationFrame(animate);
  };

  const reset = () => {
    target.current = { x: 0, y: 0 };
    if (!rafId.current) rafId.current = requestAnimationFrame(animate);
  };

  return (
    <span ref={ref} className={className} style={style} onMouseMove={move} onMouseLeave={reset} onClick={onClick}>
      {children}
    </span>
  );
}

/* ---- drag-to-scroll wrapper ---- */
function DragScroll({ children, className = "" }) {
  const ref = useRef(null);
  const drag = useRef({ down: false, startX: 0, scroll: 0, moved: 0 });
  const onDown = (e) => {
    const el = ref.current;
    drag.current = { down: true, startX: e.pageX, scroll: el.scrollLeft, moved: 0 };
    el.classList.add("drag");
  };
  const onMove = (e) => {
    if (!drag.current.down) return;
    e.preventDefault();
    const dx = e.pageX - drag.current.startX;
    drag.current.moved = Math.abs(dx);
    ref.current.scrollLeft = drag.current.scroll - dx;
  };
  const onUp = () => { drag.current.down = false; if (ref.current) ref.current.classList.remove("drag"); };
  return (
    <div ref={ref} className={className}
      onMouseDown={onDown} onMouseMove={onMove} onMouseUp={onUp} onMouseLeave={onUp}>
      {children}
    </div>
  );
}

/* ---- tiny inline icons ---- */
const Icon = {
  arrow: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M5 12h14M13 6l6 6-6 6"/></svg>,
  chevron: (p) => <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M6 9l6 6 6-6"/></svg>,
  download: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg>,
  mail: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg>,
  linkedin: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5zM3 9h4v12H3zM9 9h3.8v1.7h.05c.53-1 1.83-2.05 3.77-2.05 4.03 0 4.78 2.65 4.78 6.1V21h-4v-5.5c0-1.3-.02-3-1.83-3-1.83 0-2.11 1.43-2.11 2.9V21H9z"/></svg>,
  cal: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="3" y="4" width="18" height="17" rx="2"/><path d="M3 9h18M8 2v4M16 2v4"/></svg>,
  drag: (p) => <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M9 6l-3 3 3 3M15 6l3 3-3 3"/><path d="M6 9h12"/></svg>,
};

Object.assign(window, { Reveal, useInView, Counter, BarChart, Magnetic, DragScroll, Icon });
