/* global React */

// =============================================================================
// reactbits.jsx — selected ReactBits-inspired effects, ported to no-build
// React 18 + vanilla CSS for the Sinux Group site. No Tailwind, no
// framer-motion, no WebGL. All animation either runs on CSS keyframes or a
// single rAF/interval loop that pauses when off-screen.
//
// Tasteful subset only — chosen to match the dark editorial vibe:
//   - Aurora           (background)         drifting brand-tinted gradients
//   - SplitText        (text reveal)        per-letter cascade on intersect
//   - DecryptedText    (text reveal)        cipher decode for eyebrow labels
//   - SpotlightCard    (component wrapper)  mouse-following spotlight glow
//   - ShimmerText      (text inline)        gold/teal shimmer for brand mark
//
// Components register on window so other Babel-standalone scripts (group-home,
// capital, etc.) can reference them without imports.
// =============================================================================

// --- Aurora ------------------------------------------------------------------
// CSS-only aurora: 4 large radial-gradient orbs in brand colors, blurred,
// drifting on independent durations. mix-blend-mode: screen layers them so
// the overlap regions glow without going crunchy. No GPU shader needed.
function Aurora({ palette = "group", className = "", style }) {
  const PALETTES = {
    group:   ["#D3AF37", "#01A0A6", "#8B5CF6", "#3D82FF"],
    capital: ["#D3AF37", "#A08102", "#7C5F0F", "#D3AF37"],
    consult: ["#01A0A6", "#066469", "#10B981", "#3D82FF"],
    relay:   ["#8B5CF6", "#A78BFA", "#7C3AED", "#3D82FF"],
  };
  const colors = PALETTES[palette] || PALETTES.group;
  return (
    <div className={`rb-aurora ${className}`} aria-hidden="true" style={style}>
      <div className="rb-aurora-orb rb-aurora-orb-1" style={{ background: `radial-gradient(circle, ${colors[0]}38 0%, transparent 65%)` }} />
      <div className="rb-aurora-orb rb-aurora-orb-2" style={{ background: `radial-gradient(circle, ${colors[1]}32 0%, transparent 65%)` }} />
      <div className="rb-aurora-orb rb-aurora-orb-3" style={{ background: `radial-gradient(circle, ${colors[2]}30 0%, transparent 65%)` }} />
      <div className="rb-aurora-orb rb-aurora-orb-4" style={{ background: `radial-gradient(circle, ${colors[3]}26 0%, transparent 65%)` }} />
      <div className="rb-aurora-grain" />
    </div>
  );
}

// --- SplitText ---------------------------------------------------------------
// Letter-by-letter cascade on first viewport entry. Respects whitespace by
// not animating spaces (keeps natural kerning), and supports `as` to wrap
// in any tag so it composes with existing h1/h2 classes.
function SplitText({ text, as = "span", className = "", delay = 0, stagger = 30, style }) {
  const ref = React.useRef(null);
  const [visible, setVisible] = React.useState(false);

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(
      (entries) => {
        for (const e of entries) {
          if (e.isIntersecting) { setVisible(true); io.disconnect(); break; }
        }
      },
      { threshold: 0.2 }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);

  const Tag = as;
  const chars = (text || "").split("");
  return (
    <Tag ref={ref} className={`rb-split ${visible ? "rb-split-in" : ""} ${className}`} style={style}>
      {chars.map((ch, i) => (
        <span
          key={i}
          className="rb-split-char"
          style={{ animationDelay: `${delay + i * stagger}ms` }}
          aria-hidden={ch === " " ? "true" : undefined}
        >
          {ch === " " ? " " : ch}
        </span>
      ))}
    </Tag>
  );
}

// --- DecryptedText -----------------------------------------------------------
// Cipher decode reveal. Cycles each not-yet-revealed character through random
// glyphs from `characters` (digits + brackets + slashes by default — matches
// the Michroma display feel), then reveals left-to-right at `speed` ms/step.
// One-shot on viewport entry; honors prefers-reduced-motion.
function DecryptedText({
  text,
  speed = 38,
  maxIterations = 8,
  className = "",
  characters = "01XO/_<>{}#@%&*$+=-",
  as = "span",
}) {
  const [display, setDisplay] = React.useState(text);
  const [revealed, setRevealed] = React.useState(0);
  const [started, setStarted] = React.useState(false);
  const containerRef = React.useRef(null);

  React.useEffect(() => {
    const el = containerRef.current;
    if (!el || started) return;
    const reduced = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduced) { setRevealed(text.length); setDisplay(text); setStarted(true); return; }
    const io = new IntersectionObserver(
      (entries) => {
        for (const e of entries) {
          if (e.isIntersecting) { setStarted(true); io.disconnect(); break; }
        }
      },
      { threshold: 0.3 }
    );
    io.observe(el);
    return () => io.disconnect();
  }, [started, text]);

  React.useEffect(() => {
    if (!started) return;
    if (revealed >= text.length) return;
    let iter = 0;
    const interval = setInterval(() => {
      iter++;
      // build display: revealed prefix + cipher for the rest
      let next = "";
      for (let i = 0; i < text.length; i++) {
        if (i < revealed) { next += text[i]; continue; }
        if (text[i] === " ") { next += " "; continue; }
        next += characters[Math.floor(Math.random() * characters.length)];
      }
      setDisplay(next);
      if (iter >= maxIterations) {
        clearInterval(interval);
        setRevealed((r) => r + 1);
      }
    }, speed);
    return () => clearInterval(interval);
  }, [started, revealed, text, characters, speed, maxIterations]);

  const Tag = as;
  return (
    <Tag ref={containerRef} className={`rb-decrypt ${className}`} aria-label={text}>
      <span aria-hidden="true">{display}</span>
    </Tag>
  );
}

// --- SpotlightCard -----------------------------------------------------------
// Mouse-following radial spotlight overlay. Tracks pointer via CSS vars so
// React doesn't re-render on every move (cheap). Works with any child markup.
function SpotlightCard({ children, className = "", spotlightColor = "rgba(211,175,55,0.18)", as = "div", ...rest }) {
  const ref = React.useRef(null);
  const onMove = (e) => {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    el.style.setProperty("--rb-mx", `${e.clientX - r.left}px`);
    el.style.setProperty("--rb-my", `${e.clientY - r.top}px`);
  };
  const Tag = as;
  return (
    <Tag
      ref={ref}
      onMouseMove={onMove}
      className={`rb-spotlight ${className}`}
      style={{ "--rb-spotlight-color": spotlightColor }}
      {...rest}
    >
      {children}
    </Tag>
  );
}

// --- ShimmerText -------------------------------------------------------------
// Continuous metallic shimmer across the text. Brand mark / hero accent only —
// keep it sparing or it becomes glitter.
function ShimmerText({ children, className = "", color = "var(--accent)", baseColor = "var(--ink)", duration = 3.6 }) {
  return (
    <span
      className={`rb-shimmer ${className}`}
      style={{
        "--rb-shimmer-color": color,
        "--rb-shimmer-base": baseColor,
        "--rb-shimmer-duration": `${duration}s`,
      }}
    >
      {children}
    </span>
  );
}

// --- Waves ------------------------------------------------------------
// Monochrome flow-field canvas: low-alpha white horizontal sine lines,
// drifting. Sits behind content as quiet "engineered" texture. Pauses
// off-screen via IntersectionObserver. Respects prefers-reduced-motion.
function Waves({
  lineColor = "rgba(255,255,255,0.06)",
  lineCount = 7,
  speed = 0.00045,
  amplitude = 28,
  className = "",
  style,
}) {
  const canvasRef = React.useRef(null);
  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const reduced = typeof window !== "undefined" && window.matchMedia &&
      window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    let dpr = Math.min(window.devicePixelRatio || 1, 1.5);
    let w = 0, h = 0;
    function resize() {
      w = canvas.offsetWidth;
      h = canvas.offsetHeight;
      canvas.width = w * dpr;
      canvas.height = h * dpr;
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.scale(dpr, dpr);
    }
    resize();

    const effectiveLineCount = (w < 768) ? Math.max(3, Math.floor(lineCount / 2)) : lineCount;

    function drawFrame(t) {
      ctx.clearRect(0, 0, w, h);
      ctx.strokeStyle = lineColor;
      ctx.lineWidth = 1;
      const phase = t * speed;
      for (let i = 0; i < effectiveLineCount; i++) {
        const baseY = (h / (effectiveLineCount + 1)) * (i + 1);
        ctx.beginPath();
        for (let x = 0; x <= w; x += 6) {
          const yWave = baseY
            + Math.sin(x * 0.0085 + phase + i * 0.7) * amplitude
            + Math.cos(x * 0.0032 + phase * 0.6 + i * 0.4) * (amplitude * 0.4);
          if (x === 0) ctx.moveTo(x, yWave);
          else ctx.lineTo(x, yWave);
        }
        ctx.stroke();
      }
    }

    let rafId = 0;
    let running = false;
    function loop(t) {
      drawFrame(t);
      if (running) rafId = requestAnimationFrame(loop);
    }

    const io = new IntersectionObserver((entries) => {
      const visible = entries.some(e => e.isIntersecting);
      if (visible && !reduced && !running) {
        running = true;
        rafId = requestAnimationFrame(loop);
      } else if (!visible && running) {
        running = false;
        if (rafId) cancelAnimationFrame(rafId);
      }
    }, { threshold: 0.02 });
    io.observe(canvas);

    function onResize() { resize(); }
    window.addEventListener("resize", onResize);

    // Static single frame for reduced-motion users
    if (reduced) drawFrame(0);

    return () => {
      running = false;
      io.disconnect();
      if (rafId) cancelAnimationFrame(rafId);
      window.removeEventListener("resize", onResize);
    };
  }, [lineColor, lineCount, speed, amplitude]);

  return (
    <canvas
      ref={canvasRef}
      className={`rb-waves ${className}`}
      aria-hidden="true"
      style={{
        display: "block",
        position: "absolute",
        inset: 0,
        width: "100%",
        height: "100%",
        pointerEvents: "none",
        ...style,
      }}
    />
  );
}

window.Aurora = Aurora;
window.SplitText = SplitText;
window.DecryptedText = DecryptedText;
window.SpotlightCard = SpotlightCard;
window.ShimmerText = ShimmerText;
window.Waves = Waves;
