/* global React */
// Shared helpers for the LP UI kit. Exposes Icon, SectionHead, Reveal on window.

function Icon({ name, size = 20, stroke = 2, color = "currentColor", style = {} }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!ref.current || !window.lucide) return;
    ref.current.innerHTML = "";
    const el = document.createElement("i");
    el.setAttribute("data-lucide", name);
    ref.current.appendChild(el);
    window.lucide.createIcons({
      attrs: { width: size, height: size, "stroke-width": stroke, stroke: color },
    });
  }, [name, size, stroke, color]);
  return <span ref={ref} style={{ display: "inline-flex", lineHeight: 0, ...style }} />;
}

// useInView — shared scroll-trigger. Reveals when an element scrolls into view.
// The safety timeout ONLY force-reveals elements that are already on (or above)
// the screen at load — so genuinely off-screen sections stay hidden and animate
// in as you scroll to them (the LayerX behaviour), never silently pre-revealing.
function useInView({ threshold = 0.12, margin = "0px 0px -12% 0px", safety = 600 } = {}) {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let done = false;
    const reveal = () => { if (!done) { done = true; setShown(true); } };
    const io = new IntersectionObserver(
      (entries) => entries.forEach((e) => { if (e.isIntersecting) { reveal(); io.disconnect(); } }),
      { threshold, rootMargin: margin }
    );
    io.observe(el);
    // Fallback: reveal only if the element is within ~the first viewport at load.
    const t = setTimeout(() => {
      const r = el.getBoundingClientRect();
      if (r.top < window.innerHeight * 0.92) reveal();
    }, safety);
    return () => { io.disconnect(); clearTimeout(t); };
  }, []);
  return [ref, shown];
}

// Reveal — fades + rises its children in when scrolled into view. With
// `stagger` (seconds) the direct children cascade in one-by-one (LayerX style).
function Reveal({ children, as = "div", className = "", delay = 0, stagger = 0, ...rest }) {
  const Tag = as;
  const [ref, shown] = useInView();
  let kids = children;
  if (stagger > 0) {
    kids = React.Children.map(children, (child, i) =>
      React.isValidElement(child)
        ? React.cloneElement(child, {
            className: `lp-reveal__item ${child.props.className || ""}`.trim(),
            style: { ...(child.props.style || {}), transitionDelay: `${(i * stagger).toFixed(3)}s` },
          })
        : child
    );
  }
  return (
    <Tag
      ref={ref}
      className={`lp-reveal ${stagger > 0 ? "is-stagger" : ""} ${shown ? "is-in" : ""} ${className}`}
      style={{ "--rd": `${delay}s` }}
      {...rest}
    >
      {kids}
    </Tag>
  );
}

// SplitText — LayerX signature: each character fades up + de-blurs on a stagger
// as the line scrolls into view. `lines` is a string or array of strings (one
// per visual row). `accentLine` colors a row + (with `underline`) draws a swipe.
function SplitText({ lines, accentLine = -1, accentRange = null, underline = false, mask = false, stagger = 0.026, start = 0, className = "", as = "span" }) {
  const Tag = as;
  const [ref, shown] = useInView({ threshold: 0.25, margin: "0px 0px -6% 0px" });
  const arr = Array.isArray(lines) ? lines : [lines];
  let idx = 0;
  return (
    <Tag ref={ref} className={`lp-split ${mask ? "lp-split--mask" : ""} ${shown ? "is-in" : ""} ${className}`}>
      {arr.map((line, li) => (
        <span key={li} className={`lp-split__line ${li === accentLine ? "lp-split__line--accent" : ""}`}>
          {Array.from(String(line)).map((ch, ci) => {
            const d = (start + idx * stagger).toFixed(3); idx += 1;
            const acc = accentRange && li === accentRange.line && ci >= accentRange.from && ci < accentRange.to;
            const latin = /[A-Za-z0-9]/.test(ch);
            return (
              <span key={ci} className={`lp-split__c ${acc ? "lp-split__c--accent" : ""} ${latin ? "lp-split__c--latin" : ""}`} style={{ transitionDelay: `${d}s` }}>
                {ch === " " ? "\u00A0" : ch}
              </span>
            );
          })}
          {underline && li === accentLine ? <i className="lp-split__u" /> : null}
        </span>
      ))}
    </Tag>
  );
}

// useIsMobile — true when viewport is at/below `bp` px. Drives mobile-only
// layouts (accordions, carousel) without touching the desktop render path.
function useIsMobile(bp = 768) {
  const q = `(max-width:${bp}px)`;
  const [m, setM] = React.useState(
    () => typeof window !== "undefined" && window.matchMedia(q).matches
  );
  React.useEffect(() => {
    const mq = window.matchMedia(q);
    const on = () => setM(mq.matches);
    on();
    mq.addEventListener ? mq.addEventListener("change", on) : mq.addListener(on);
    return () => {
      mq.removeEventListener ? mq.removeEventListener("change", on) : mq.removeListener(on);
    };
  }, [q]);
  return m;
}

// Accordion — mobile collapsible list. `items` = [{ key, head, body }].
// `defaultOpen` = array of keys open on first render. Smooth height via the
// grid-template-rows 0fr→1fr trick (no max-height guesswork).
function Accordion({ items, defaultOpen = [] }) {
  const [open, setOpen] = React.useState(() => new Set(defaultOpen));
  const toggle = (k) =>
    setOpen((s) => {
      const n = new Set(s);
      n.has(k) ? n.delete(k) : n.add(k);
      return n;
    });
  return (
    <div className="lp-acc">
      {items.map((it) => {
        const isOpen = open.has(it.key);
        return (
          <div key={it.key} className={`lp-acc__item ${isOpen ? "is-open" : ""}`}>
            <button
              type="button"
              className="lp-acc__head"
              aria-expanded={isOpen}
              onClick={() => toggle(it.key)}
            >
              <span className="lp-acc__headinner">{it.head}</span>
              <span className="lp-acc__chev"><Icon name="chevron-down" size={20} color="var(--accent)" /></span>
            </button>
            <div className="lp-acc__panel">
              <div className="lp-acc__panelin">{it.body}</div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ScrollHint — the small "Scroll" cue with a sweeping accent line.
function ScrollHint({ label = "Scroll" }) {
  return (
    <div className="lp-scrollhint" aria-hidden="true">
      <span className="lp-scrollhint__label">{label}</span>
      <span className="lp-scrollhint__line"><i /></span>
    </div>
  );
}

// Eyebrow + serif headline that opens each section, with an index marker.
function SectionHead({ index, eyebrow, title, lead, tone = "blue", align = "split" }) {
  const { Eyebrow } = window.AIDesignSystem_dabded;
  return (
    <div className={`lp-head lp-head--${align}`}>
      <div>
        <div className="lp-head__eyebrow">
          {index && <span className="lp-head__idx">{index}</span>}
          <Eyebrow tone={tone}>{eyebrow}</Eyebrow>
        </div>
        <h2 className="lp-h2 ha-serif"><SplitText lines={title} stagger={0.016} /></h2>
      </div>
      {lead && <p className="lp-lead" data-parallax="0.05">{lead}</p>}
    </div>
  );
}

// OrbitField — kit-local animated orbit (rings + nodes travelling on tracks).
// Self-contained so the hero scene animates regardless of bundle timing.
function OrbitField({ size = 420 }) {
  const rings = [
    { inset: 0, color: "rgba(127,178,255,.40)" },
    { inset: size * 0.17, color: "rgba(127,178,255,.22)" },
  ];
  const tracks = [
    { d: size, dur: 34, rev: false, node: 14, glow: true, delay: 0 },
    { d: size * 0.66, dur: 24, rev: true, node: 12, glow: true, delay: -6 },
    { d: size * 0.66, dur: 24, rev: true, node: 8, glow: false, delay: -15 },
    { d: size * 0.30, dur: 16, rev: false, node: 9, glow: false, delay: -3 },
  ];
  return (
    <div className="lp-orbf" style={{ width: size, height: size }}>
      {rings.map((r, i) => (
        <span key={`r${i}`} className="lp-orbf__ring" style={{ inset: r.inset, borderColor: r.color }} />
      ))}
      <span className="lp-orbf__ring lp-orbf__ring--dash" style={{ inset: size * 0.35 }} />
      {tracks.map((t, i) => (
        <div
          key={`t${i}`}
          className="lp-orbf__track"
          style={{ width: t.d, height: t.d, marginTop: -t.d / 2, marginLeft: -t.d / 2, animationDuration: `${t.dur}s`, animationDirection: t.rev ? "reverse" : "normal", animationDelay: `${t.delay}s` }}
        >
          <span
            className="lp-orbf__node"
            style={{ width: t.node, height: t.node, marginLeft: -t.node / 2, top: -t.node / 2, background: t.glow ? "var(--blue-600)" : "var(--blue-400)", boxShadow: t.glow ? "var(--shadow-node)" : "none" }}
          />
        </div>
      ))}
    </div>
  );
}

Object.assign(window, { Icon, Reveal, SplitText, ScrollHint, useInView, useIsMobile, Accordion, SectionHead, OrbitField });
