// Arise Helix — shared primitives.

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

/* ──────────────── Scroll + viewport hooks ──────────────── */

function useScrollY() {
  const [y, setY] = useState(0);
  useEffect(() => {
    const onScroll = () => setY(window.scrollY);
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  return y;
}

function useInViewOnce(rootMargin = "0px") {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            setInView(true);
            obs.disconnect();
          }
        });
      },
      { rootMargin, threshold: 0.05 }
    );
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, [rootMargin]);
  return [ref, inView];
}

function useScrollProgressOn(ref, startVP = 0.85, endVP = 0.2) {
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    const onScroll = () => {
      const el = ref.current;
      if (!el) return;
      const rect = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const startScrollY = window.scrollY + (rect.top - vh * startVP);
      const endScrollY = window.scrollY + (rect.bottom - vh * endVP);
      const total = endScrollY - startScrollY;
      if (total <= 0) return setProgress(0);
      const p = (window.scrollY - startScrollY) / total;
      setProgress(Math.max(0, Math.min(1, p)));
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, [ref, startVP, endVP]);
  return progress;
}

// Section index detector — which of the registered sections is currently dominant in the viewport
function useActiveSection(sectionIds) {
  const [active, setActive] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const vh = window.innerHeight;
      const center = window.scrollY + vh / 2;
      let bestIdx = 0;
      let bestDist = Infinity;
      sectionIds.forEach((id, i) => {
        const el = document.getElementById(id);
        if (!el) return;
        const top = el.offsetTop;
        const bottom = top + el.offsetHeight;
        const sectionCenter = (top + bottom) / 2;
        const dist = Math.abs(sectionCenter - center);
        if (dist < bestDist) {
          bestDist = dist;
          bestIdx = i;
        }
      });
      setActive(bestIdx);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, [JSON.stringify(sectionIds)]);
  return active;
}

// Page scroll progress 0..1 across whole document
function usePageProgress() {
  const [p, setP] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const total = document.body.scrollHeight - window.innerHeight;
      if (total <= 0) return setP(0);
      setP(Math.max(0, Math.min(1, window.scrollY / total)));
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, []);
  return p;
}

// Cursor position (for magnetic / radial glow)
function useCursor() {
  const [pos, setPos] = useState({ x: -1, y: -1 });
  useEffect(() => {
    const onMove = (e) => setPos({ x: e.clientX, y: e.clientY });
    window.addEventListener("pointermove", onMove);
    return () => window.removeEventListener("pointermove", onMove);
  }, []);
  return pos;
}

/* ──────────────── Text reveal primitives ──────────────── */

function WordsPullUp({ text, className = "", style = {}, stagger = 0.06, delayBase = 0 }) {
  const [ref, inView] = useInViewOnce("-40px");
  const words = useMemo(() => text.split(" "), [text]);
  return (
    <div ref={ref} className={className} style={style}>
      <span style={{ display: "inline-flex", flexWrap: "wrap" }}>
        {words.map((w, i) => (
          <span key={i} style={{ display: "inline-block", overflow: "visible", marginRight: "0.22em" }}>
            <span
              style={{
                display: "inline-block",
                transform: inView ? "translateY(0)" : "translateY(28px)",
                opacity: inView ? 1 : 0,
                transition: `transform 0.9s cubic-bezier(0.16, 1, 0.3, 1) ${delayBase + i * stagger}s, opacity 0.9s cubic-bezier(0.16, 1, 0.3, 1) ${delayBase + i * stagger}s`,
                whiteSpace: "pre",
              }}
            >
              {w}
            </span>
          </span>
        ))}
      </span>
    </div>
  );
}

function MixedHeading({ parts, className = "", style = {}, stagger = 0.06 }) {
  // parts: [{ text, italic }]
  const [ref, inView] = useInViewOnce("-40px");
  const items = useMemo(() => {
    const out = [];
    parts.forEach((p) => {
      p.text.split(" ").filter((w) => w.length).forEach((w) => out.push({ w, italic: !!p.italic }));
    });
    return out;
  }, [parts]);
  return (
    <h2 ref={ref} className={className} style={style}>
      <span style={{ display: "inline-flex", flexWrap: "wrap" }}>
        {items.map((it, i) => (
          <span key={i} style={{ display: "inline-block", overflow: "visible", marginRight: "0.22em" }}>
            <span
              className={it.italic ? "helix-serif" : ""}
              style={{
                display: "inline-block",
                transform: inView ? "translateY(0)" : "translateY(28px)",
                opacity: inView ? 1 : 0,
                transition: `transform 0.95s cubic-bezier(0.16, 1, 0.3, 1) ${i * stagger}s, opacity 0.95s cubic-bezier(0.16, 1, 0.3, 1) ${i * stagger}s`,
                whiteSpace: "pre",
              }}
            >
              {it.w}
            </span>
          </span>
        ))}
      </span>
    </h2>
  );
}

function AnimatedLetters({ text, className = "", style = {} }) {
  const ref = useRef(null);
  const progress = useScrollProgressOn(ref, 0.8, 0.25);
  const chars = useMemo(() => Array.from(text), [text]);
  const total = chars.length;
  return (
    <p ref={ref} className={className} style={style}>
      {chars.map((c, i) => {
        const cp = i / total;
        const start = cp - 0.1;
        const end = cp + 0.05;
        let o = 0.15;
        if (progress >= end) o = 1;
        else if (progress > start) o = 0.15 + ((progress - start) / (end - start)) * 0.85;
        return (
          <span
            key={i}
            style={{
              opacity: o,
              transition: "opacity 0.18s linear",
              whiteSpace: c === " " ? "pre" : "normal",
            }}
          >
            {c}
          </span>
        );
      })}
    </p>
  );
}

function FadeUp({ children, delay = 0, y = 22, className = "", style = {}, as = "div" }) {
  const [ref, inView] = useInViewOnce("-40px");
  const Tag = as;
  return (
    <Tag
      ref={ref}
      className={className}
      style={{
        ...style,
        transform: inView ? "translateY(0)" : `translateY(${y}px)`,
        opacity: inView ? 1 : 0,
        transition: `transform 0.95s cubic-bezier(0.16, 1, 0.3, 1) ${delay}s, opacity 0.95s cubic-bezier(0.16, 1, 0.3, 1) ${delay}s`,
      }}
    >
      {children}
    </Tag>
  );
}

function InViewScale({ children, delay = 0, className = "", style = {}, from = 0.96 }) {
  const [ref, inView] = useInViewOnce("-80px");
  return (
    <div
      ref={ref}
      className={className}
      style={{
        ...style,
        transform: inView ? "scale(1)" : `scale(${from})`,
        opacity: inView ? 1 : 0,
        transition: `transform 0.95s cubic-bezier(0.22, 1, 0.36, 1) ${delay}s, opacity 0.95s cubic-bezier(0.22, 1, 0.36, 1) ${delay}s`,
      }}
    >
      {children}
    </div>
  );
}

function StatCounter({ to, suffix = "", duration = 1800, className = "", style = {}, formatter }) {
  const [ref, inView] = useInViewOnce("-60px");
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!inView) return;
    const start = performance.now();
    let raf;
    const tick = (now) => {
      const t = Math.min(1, (now - start) / duration);
      const eased = 1 - Math.pow(1 - t, 3);
      setVal(to * eased);
      if (t < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView, to, duration]);
  const display = formatter ? formatter(val) : Math.round(val).toLocaleString();
  return <span ref={ref} className={className} style={style}>{display}{suffix}</span>;
}

/* ──────────────── Magnetic button ──────────────── */

function MagneticButton({ children, className = "", style = {}, onClick, intensity = 0.4 }) {
  const ref = useRef(null);
  const [t, setT] = useState({ x: 0, y: 0 });
  const onMove = (e) => {
    const r = ref.current.getBoundingClientRect();
    const cx = r.left + r.width / 2;
    const cy = r.top + r.height / 2;
    setT({ x: (e.clientX - cx) * intensity, y: (e.clientY - cy) * intensity });
  };
  const reset = () => setT({ x: 0, y: 0 });
  return (
    <button
      ref={ref}
      onMouseMove={onMove}
      onMouseLeave={reset}
      onClick={onClick}
      className={className}
      style={{
        ...style,
        transform: `translate3d(${t.x}px, ${t.y}px, 0)`,
        transition: "transform 0.25s cubic-bezier(0.16, 1, 0.3, 1)",
      }}
    >
      {children}
    </button>
  );
}

/* ──────────────── Icons (lucide-style) ──────────────── */

const iconBase = { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round" };
const I = (size = 18, sx = {}, cls = "") => ({ ...iconBase, width: size, height: size, style: sx, className: cls });

const IconArrowRight = ({ size, style, className }) => (<svg {...I(size||18, style, className)}><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>);
const IconArrowUpRight = ({ size, style, className }) => (<svg {...I(size||18, style, className)}><path d="M7 17 17 7"/><path d="M7 7h10v10"/></svg>);
const IconCheck = ({ size, style, className }) => (<svg {...I(size||14, style, className)} strokeWidth={2.4}><path d="M20 6 9 17l-5-5"/></svg>);
const IconPlay = ({ size, style, className }) => (<svg {...I(size||18, style, className)} fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg>);
const IconStethoscope = ({ size, style, className }) => (<svg {...I(size||22, style, className)}><path d="M11 2v3a4 4 0 0 0 4 4h0a4 4 0 0 0 4-4V2"/><path d="M11 2v3a4 4 0 0 1-4 4h0a4 4 0 0 1-4-4V2"/><path d="M7 9v4a5 5 0 0 0 10 0"/><circle cx="17" cy="20" r="2"/><path d="M17 18v-3"/></svg>);
const IconBookOpen = ({ size, style, className }) => (<svg {...I(size||22, style, className)}><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h4a4 4 0 0 1 5 4 4 4 0 0 1 5-4h4a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-5a3 3 0 0 0-4 0H3z"/></svg>);
const IconLayers = ({ size, style, className }) => (<svg {...I(size||22, style, className)}><path d="m12 2 9 5-9 5-9-5 9-5Z"/><path d="m3 12 9 5 9-5"/><path d="m3 17 9 5 9-5"/></svg>);
const IconCalendar = ({ size, style, className }) => (<svg {...I(size||18, style, className)}><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M16 2v4"/><path d="M8 2v4"/><path d="M3 10h18"/></svg>);
const IconSmartphone = ({ size, style, className }) => (<svg {...I(size||22, style, className)}><rect width="14" height="20" x="5" y="2" rx="2.5"/><path d="M12 18h.01"/></svg>);
const IconGlobe = ({ size, style, className }) => (<svg {...I(size||22, style, className)}><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15 15 0 0 1 0 20"/><path d="M12 2a15 15 0 0 0 0 20"/></svg>);
const IconActivity = ({ size, style, className }) => (<svg {...I(size||22, style, className)}><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>);
const IconBeaker = ({ size, style, className }) => (<svg {...I(size||22, style, className)}><path d="M4.5 3h15"/><path d="M6 3v16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V3"/><path d="M6 14h12"/></svg>);
const IconWaveform = ({ size, style, className }) => (<svg {...I(size||22, style, className)}><path d="M2 12h2l3-9 4 18 3-12 2 6h6"/></svg>);
const IconPhone = ({ size, style, className }) => (<svg {...I(size||18, style, className)}><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92Z"/></svg>);
const IconMail = ({ size, style, className }) => (<svg {...I(size||18, style, className)}><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-10 6L2 7"/></svg>);
const IconMapPin = ({ size, style, className }) => (<svg {...I(size||18, style, className)}><path d="M20 10c0 7-8 12-8 12s-8-5-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>);
const IconQuote = ({ size, style, className }) => (<svg {...I(size||24, style, className)} fill="currentColor" stroke="none"><path d="M9 7H5a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2v2c0 2.21-1.79 4-4 4v2c3.31 0 6-2.69 6-6V9a2 2 0 0 0 0-2zm12 0h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2v2c0 2.21-1.79 4-4 4v2c3.31 0 6-2.69 6-6V9a2 2 0 0 0 0-2z"/></svg>);
const IconSparkle = ({ size, style, className }) => (<svg {...I(size||18, style, className)} fill="currentColor" stroke="none"><path d="M12 2 13.6 8.4 20 10l-6.4 1.6L12 18l-1.6-6.4L4 10l6.4-1.6L12 2z"/></svg>);

Object.assign(window, {
  useScrollY, useInViewOnce, useScrollProgressOn, useActiveSection, usePageProgress, useCursor,
  WordsPullUp, MixedHeading, AnimatedLetters, FadeUp, InViewScale, StatCounter, MagneticButton,
  IconArrowRight, IconArrowUpRight, IconCheck, IconPlay, IconStethoscope, IconBookOpen, IconLayers,
  IconCalendar, IconSmartphone, IconGlobe, IconActivity, IconBeaker, IconWaveform,
  IconPhone, IconMail, IconMapPin, IconQuote, IconSparkle,
});
