/* global React, FramerMotion */
const { motion, useInView, useScroll, useTransform, AnimatePresence } = FramerMotion;
const { useRef, useState, useEffect, useMemo } = React;

const EASE_OUT_QUART = [0.22, 1, 0.36, 1];
const EASE_EXPO     = [0.16, 1, 0.3, 1];

/* If the document is backgrounded at boot, the browser throttles RAF/WAAPI
   to 0fps and animations never commit — leaving the page stuck on initial
   states. In that case we fast-forward: components mount in their final
   state with zero-duration animations. Real browser tabs (visible) animate
   normally. */
const SKIP_ANIM = (typeof document !== "undefined" && document.hidden);
// MotionGlobalConfig.skipAnimations is also set in the HTML boot script,
// so framer-motion bypasses transitions. We keep SKIP_ANIM as a flag for
// our own custom looping animations (chat stream, dashboard counter) so
// they don't run heavy timers while hidden either.

/* useDeferredMount — return true after a small delay. */
function useDeferredMount(delay = 0) {
  const [ready, setReady] = useState(SKIP_ANIM);
  useEffect(() => {
    if (SKIP_ANIM) return;
    const id = setTimeout(() => setReady(true), 30 + delay);
    return () => clearTimeout(id);
  }, [delay]);
  return ready;
}

/* Helper to merge a base transition with the skip-anim override */
function tx(base) {
  return SKIP_ANIM ? { duration: 0 } : base;
}

/* WordsPullUp — splits text by spaces, each word slides up. */
function WordsPullUp({ text, className = "", delay = 0, stagger = 0.08, showAsterisk = false, asteriskClass = "" }) {
  const ready = useDeferredMount();
  const words = text.split(" ");
  if (SKIP_ANIM) {
    return (
      <span className={className} style={{ display: "inline-flex", flexWrap: "wrap", gap: "0.22em" }}>
        {words.map((w, i) => {
          const isLast = i === words.length - 1;
          return (
            <span key={i} style={{ display: "inline-block", position: "relative" }}>
              {w}
              {showAsterisk && isLast && (
                <span className={asteriskClass} style={{ position: "absolute", top: "0.15em", right: "-0.35em", fontSize: "0.31em" }}>*</span>
              )}
            </span>
          );
        })}
      </span>
    );
  }
  return (
    <span className={className} style={{ display: "inline-flex", flexWrap: "wrap", gap: "0.22em" }}>
      {words.map((w, i) => {
        const isLast = i === words.length - 1;
        return (
          <span key={i} style={{ display: "inline-block", overflow: "hidden", lineHeight: "1.18", paddingBottom: "0.06em" }}>
            <motion.span
              style={{ display: "inline-block", position: "relative" }}
              initial={{ y: 28, opacity: 0 }}
              animate={ready ? { y: 0, opacity: 1 } : { y: 28, opacity: 0 }}
              transition={{ duration: 0.9, ease: EASE_EXPO, delay: delay + i * stagger }}
            >
              {w}
              {showAsterisk && isLast && (
                <span
                  className={asteriskClass}
                  style={{
                    position: "absolute",
                    top: "0.15em",
                    right: "-0.35em",
                    fontSize: "0.31em",
                  }}
                >*</span>
              )}
            </motion.span>
          </span>
        );
      })}
    </span>
  );
}

/* WordsPullUpMultiStyle — array of {text, className} segments. */
function WordsPullUpMultiStyle({ segments, className = "", delay = 0, stagger = 0.06, justify = "center" }) {
  const ready = useDeferredMount();
  const flat = [];
  segments.forEach((seg) => {
    seg.text.split(" ").forEach((w) => {
      if (w) flat.push({ word: w, cls: seg.className || "" });
    });
  });
  if (SKIP_ANIM) {
    return (
      <span className={className} style={{ display: "inline-flex", flexWrap: "wrap", gap: "0.22em", justifyContent: justify }}>
        {flat.map((item, i) => (
          <span key={i} className={item.cls} style={{ display: "inline-block" }}>
            {item.word}
          </span>
        ))}
      </span>
    );
  }
  return (
    <span
      className={className}
      style={{
        display: "inline-flex",
        flexWrap: "wrap",
        gap: "0.22em",
        justifyContent: justify,
      }}
    >
      {flat.map((item, i) => (
        <span key={i} style={{ display: "inline-block", overflow: "hidden", lineHeight: "1.18", paddingBottom: "0.06em" }}>
          <motion.span
            className={item.cls}
            style={{ display: "inline-block" }}
            initial={{ y: 28, opacity: 0 }}
            animate={ready ? { y: 0, opacity: 1 } : { y: 28, opacity: 0 }}
            transition={{ duration: 0.9, ease: EASE_EXPO, delay: delay + i * stagger }}
          >
            {item.word}
          </motion.span>
        </span>
      ))}
    </span>
  );
}

/* AnimatedParagraph — scroll-linked per-char opacity reveal */
function AnimatedParagraph({ text, className = "" }) {
  if (SKIP_ANIM) {
    return <p className={className}>{text}</p>;
  }
  const ref = useRef(null);
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ["start 0.85", "end 0.35"],
  });
  const chars = useMemo(() => Array.from(text), [text]);
  return (
    <p ref={ref} className={className}>
      {chars.map((ch, i) => {
        const p = i / chars.length;
        const range = [Math.max(0, p - 0.08), Math.min(1, p + 0.06)];
        return (
          <AnimatedChar key={i} char={ch} progress={scrollYProgress} range={range} />
        );
      })}
    </p>
  );
}
function AnimatedChar({ char, progress, range }) {
  const opacity = useTransform(progress, range, [0.18, 1]);
  return <motion.span style={{ opacity }}>{char}</motion.span>;
}

/* FadeUp — small reusable wrapper, animates on mount */
function FadeUp({ children, delay = 0, y = 20, duration = 0.9, className = "" }) {
  const ready = useDeferredMount();
  if (SKIP_ANIM) {
    return <div className={className}>{children}</div>;
  }
  return (
    <motion.div
      className={className}
      initial={{ y, opacity: 0 }}
      animate={ready ? { y: 0, opacity: 1 } : { y, opacity: 0 }}
      transition={{ duration, ease: EASE_EXPO, delay }}
    >
      {children}
    </motion.div>
  );
}

/* ===== ICONS ===== */
function IconArrowRight({ className = "w-4 h-4", style }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" className={className} style={style}>
      <path d="M5 12h14" />
      <path d="M13 6l6 6-6 6" />
    </svg>
  );
}
function IconCheck({ className = "w-4 h-4" }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
      <path d="M20 6L9 17l-5-5" />
    </svg>
  );
}
function IconPlus({ className = "w-4 h-4" }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className={className}>
      <path d="M12 5v14M5 12h14" />
    </svg>
  );
}
function IconMinus({ className = "w-4 h-4" }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" className={className}>
      <path d="M5 12h14" />
    </svg>
  );
}
function IconSparkle({ className = "w-4 h-4" }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className={className}>
      <path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8L12 3z" />
    </svg>
  );
}

/* useCountUp — animates an integer from 0 to target. */
function useCountUp(target, duration = 1800, deps = []) {
  const [val, setVal] = useState(SKIP_ANIM ? target : 0);
  useEffect(() => {
    if (SKIP_ANIM) { setVal(target); return; }
    let raf;
    const start = performance.now();
    const tick = (t) => {
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(Math.round(eased * target));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps);
  return val;
}

/* AnimatedSvgUnderline — wavy underline that draws itself */
function AnimatedSvgUnderline({ color = "currentColor", className = "" }) {
  return (
    <svg
      viewBox="0 0 200 8"
      preserveAspectRatio="none"
      className={className}
      style={{ height: "0.22em", width: "100%" }}
      aria-hidden
    >
      <path
        d="M 2 5 Q 25 1 50 5 T 100 5 T 150 5 T 198 5"
        stroke={color}
        strokeWidth="2.5"
        strokeLinecap="round"
        fill="none"
        className="draw-path"
        style={{ "--len": 220 }}
      />
    </svg>
  );
}

Object.assign(window, {
  WordsPullUp, WordsPullUpMultiStyle, AnimatedParagraph, FadeUp, useDeferredMount,
  IconArrowRight, IconCheck, IconPlus, IconMinus, IconSparkle,
  EASE_OUT_QUART, EASE_EXPO, SKIP_ANIM,
  useCountUp, AnimatedSvgUnderline,
});
