/* global React */
/**
 * Animations layer — upgrades for TargetSaver site
 * - WordReveal: scroll-triggered word-by-word reveal for story paragraphs
 * - LoadingCinematic: boot sequence — logo assemble + fade
 * - CountUp: animated number counter
 *
 * NOTE: MagneticCursor was removed (perf issues on Safari macOS).
 * Native cursor only.
 */
const { useState: useStateA, useEffect: useEffectA, useRef: useRefA } = React;

// ============================================================
// WordReveal — splits children into words, reveals on scroll
// ============================================================
function WordReveal({ children, className = "", as = "p", delay = 0 }) {
  const ref = useRefA(null);
  const [inView, setInView] = useStateA(false);

  useEffectA(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(
      ([entry]) => { if (entry.isIntersecting) setInView(true); },
      { threshold: 0.25 }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);

  const text = typeof children === "string" ? children : "";
  const words = text.split(/(\s+)/); // keep whitespace

  const Comp = as;
  return (
    <Comp ref={ref} className={`word-reveal ${inView ? "in" : ""} ${className}`}>
      {words.map((w, i) => {
        if (/^\s+$/.test(w)) return w;
        return (
          <span key={i} className="wr-word">
            <span
              className="wr-inner"
              style={{ transitionDelay: `${delay + i * 18}ms` }}
            >
              {w}
            </span>
          </span>
        );
      })}
    </Comp>
  );
}

// ============================================================
// LoadingCinematic — logo assemble then fade
// ============================================================
function LoadingCinematic() {
  const [stage, setStage] = useStateA(0); // 0: loading, 1: logo shown, 2: fading, 3: done

  useEffectA(() => {
    // Skip on subsequent visits in same session
    if (sessionStorage.getItem("ts_loaded")) {
      setStage(3);
      return;
    }
    const t1 = setTimeout(() => setStage(1), 80);
    const t2 = setTimeout(() => setStage(2), 1800);
    const t3 = setTimeout(() => {
      setStage(3);
      sessionStorage.setItem("ts_loaded", "1");
    }, 2500);
    return () => { clearTimeout(t1); clearTimeout(t2); clearTimeout(t3); };
  }, []);

  if (stage === 3) return null;

  return (
    <div className={`loader ${stage >= 1 ? "active" : ""} ${stage >= 2 ? "fading" : ""}`}>
      <div className="loader-logo">
        <svg viewBox="0 0 120 120" width="120" height="120" className="loader-svg">
          <circle cx="60" cy="60" r="50" fill="none" stroke="currentColor" strokeWidth="1.5" className="loader-ring r1" />
          <circle cx="60" cy="60" r="30" fill="none" stroke="currentColor" strokeWidth="1.5" className="loader-ring r2" />
          <circle cx="60" cy="60" r="8" fill="currentColor" className="loader-dot" />
        </svg>
      </div>
      <div className="loader-text">TargetSaver</div>
    </div>
  );
}

// ============================================================
// CountUp — animated number
// ============================================================
function CountUp({ value, duration = 1200, format = n => n.toLocaleString("fr-FR"), prefix = "", suffix = "" }) {
  const [display, setDisplay] = useStateA(0);
  const ref = useRefA(null);
  const [inView, setInView] = useStateA(false);

  useEffectA(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(
      ([entry]) => { if (entry.isIntersecting) setInView(true); },
      { threshold: 0.4 }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);

  useEffectA(() => {
    if (!inView) return;
    const start = performance.now();
    let raf;
    const tick = (now) => {
      const t = Math.min((now - start) / duration, 1);
      const eased = 1 - Math.pow(1 - t, 3);
      setDisplay(Math.round(eased * value));
      if (t < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView, value, duration]);

  return <span ref={ref}>{prefix}{format(display)}{suffix}</span>;
}

// Expose
Object.assign(window, { WordReveal, LoadingCinematic, CountUp });
