/* lib.jsx — shared hooks + primitives, exported to window */
const { useState, useEffect, useRef, useCallback } = React;

/* visibility via scroll (IntersectionObserver is unreliable in this env) */
function isNear(el, margin = 200) {
  if (!el) return false;
  const r = el.getBoundingClientRect();
  return r.bottom > -margin && r.top < window.innerHeight + margin;
}
function onViewportChange(fn) {
  window.addEventListener("scroll", fn, { passive: true });
  window.addEventListener("resize", fn);
  return () => {
    window.removeEventListener("scroll", fn);
    window.removeEventListener("resize", fn);
  };
}

/* Create + manage a PoolCanvas instance; start/stop on visibility */
function usePoolCanvas(mode) {
  const canvasRef = useRef(null);
  const instRef = useRef(null);

  useEffect(() => {
    if (!canvasRef.current || !window.PoolCanvas) return;
    const inst = new window.PoolCanvas(canvasRef.current);
    inst.setMode(mode);
    instRef.current = inst;
    (window.__pools = window.__pools || []).push(inst);
    const update = () => {
      if (isNear(canvasRef.current, 100)) inst.start();
      else inst.stop();
    };
    update();
    const off = onViewportChange(update);
    return () => { off(); inst.destroy(); };
  }, []);

  return { canvasRef, instRef };
}

/* scroll progress (0..1) of an element across the viewport while pinned */
function useScrollProgress(ref) {
  const [p, setP] = useState(0);
  useEffect(() => {
    let raf = null;
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => {
        raf = null;
        const el = ref.current;
        if (!el) return;
        const r = el.getBoundingClientRect();
        const total = el.offsetHeight - window.innerHeight;
        const scrolled = clampN(-r.top, 0, total);
        setP(total > 0 ? scrolled / total : 0);
      });
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, [ref]);
  return p;
}

const clampN = (v, a, b) => Math.max(a, Math.min(b, v));

/* reveal-on-scroll wrapper */
function Reveal({ children, delay = 0, as = "div", className = "", style = {} }) {
  const ref = useRef(null);
  const [vis, setVis] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const check = () => {
      if (isNear(el, 80)) { setVis(true); return true; }
      return false;
    };
    if (check()) return;
    const update = () => { if (check()) off(); };
    const off = onViewportChange(update);
    const safety = setTimeout(() => setVis(true), 1500);
    return () => { off(); clearTimeout(safety); };
  }, []);
  const Tag = as;
  return (
    <Tag
      ref={ref}
      className={className}
      style={{
        ...style,
        opacity: vis ? 1 : 0,
        transform: vis ? "translateY(0)" : "translateY(22px)",
        transition: `opacity .7s cubic-bezier(.2,.7,.2,1) ${delay}s, transform .7s cubic-bezier(.2,.7,.2,1) ${delay}s`,
      }}
    >
      {children}
    </Tag>
  );
}

/* terminal corner brackets frame */
function Frame({ children, className = "", style = {}, color = "var(--green)" }) {
  const c = { position: "absolute", width: 11, height: 11, borderColor: color, opacity: 0.55 };
  return (
    <div className={className} style={{ position: "relative", ...style }}>
      <span style={{ ...c, top: -1, left: -1, borderTop: "1px solid", borderLeft: "1px solid" }} />
      <span style={{ ...c, top: -1, right: -1, borderTop: "1px solid", borderRight: "1px solid" }} />
      <span style={{ ...c, bottom: -1, left: -1, borderBottom: "1px solid", borderLeft: "1px solid" }} />
      <span style={{ ...c, bottom: -1, right: -1, borderBottom: "1px solid", borderRight: "1px solid" }} />
      {children}
    </div>
  );
}

/* count-up number for stats */
function useCountUp(target, dur = 1400, start) {
  const [v, setV] = useState(0);
  useEffect(() => {
    if (!start) return;
    let raf, t0;
    const tick = (t) => {
      if (!t0) t0 = t;
      const k = clampN((t - t0) / dur, 0, 1);
      const e = 1 - Math.pow(1 - k, 3);
      setV(target * e);
      if (k < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [start, target]);
  return v;
}

Object.assign(window, { usePoolCanvas, useScrollProgress, useCountUp, clampN, Reveal, Frame, isNear, onViewportChange });
