// Animace inspirované cinematic landing page — BlurText, scroll reveal, motion stagger
// Vanilla impl bez framer-motion (přes IntersectionObserver + CSS transitions)
//
// FALLBACK: Každá komponenta má 2.5s safety timer. Pokud observer nezahlasuje
// (např. kvůli mobile bugům, transform translatu rodiče, display:contents), po 2.5s
// se manuálně přepne na is-in. Worst case: animace se nespustí, ale obsah je viditelný.

const { useEffect, useRef, useState } = React;

// Safety fallback: pokud IntersectionObserver z nějakého důvodu nezahlasuje
// (např. display:contents na mobile), po této době se element manuálně ukáže.
// Dříve bylo 2.5s — to ale odpalovalo VŠECHNY reveals předčasně, takže když
// uživatel pak scrolloval dolů, animace už dávno proběhly mimo viewport.
// 15s = dost času na scroll uživatele, observer normálně dohnal dříve.
const REVEAL_FALLBACK_MS = 15000;

// Helper: počká dokud preloader úplně nezmizí (včetně 700ms fade-out),
// pak spustí callback. Bez delay by animace startovaly POD ještě fadeujícím
// preloaderem a uživatel by neviděl jejich začátek.
const POST_PRELOADER_DELAY_MS = 700;
function waitForPreloader(cb) {
  if (typeof window === "undefined") { cb(); return; }
  // Preloader úplně pryč z DOM (po 700ms fade odstraněný) = animace ihned, žádný delay
  if (!document.getElementById("ds-preloader")) {
    cb();
    return;
  }
  // Preloader stále v DOM. Pokud event už firenul (preloader fadeuje), počkej na fade
  if (window.__dsPreloaderDone) {
    setTimeout(cb, POST_PRELOADER_DELAY_MS);
    return;
  }
  // Event ještě nefirenul — wait + post-fade delay
  // Safety fallback: 11s (preloader MAX_DISPLAY je 10s, dáváme rezervu)
  let fired = false;
  const fire = () => { if (!fired) { fired = true; setTimeout(cb, POST_PRELOADER_DELAY_MS); } };
  window.addEventListener("ds-preloader-done", fire, { once: true });
  setTimeout(fire, 11000);
}

// ===== BlurText: word-by-word blur reveal =====
function BlurText({ text, className = "", delay = 0, stagger = 100, as = "span", style = {} }) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);
  const words = text.split(" ");

  useEffect(() => {
    if (!ref.current) return;
    const rect = ref.current.getBoundingClientRect();
    if (rect.top < window.innerHeight && rect.bottom > 0) {
      waitForPreloader(() => setVisible(true));
      return;
    }
    let fired = false;
    const fire = () => { if (!fired) { fired = true; setVisible(true); } };
    const fallback = setTimeout(fire, REVEAL_FALLBACK_MS);
    const obs = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) { fire(); obs.disconnect(); clearTimeout(fallback); }
      },
      { threshold: 0.1 }
    );
    obs.observe(ref.current);
    return () => { obs.disconnect(); clearTimeout(fallback); };
  }, []);

  const Tag = as;
  return (
    <Tag ref={ref} className={`blur-text ${className}`} style={style}>
      {words.map((w, i) => (
        <span
          key={i}
          className={`blur-word ${visible ? "is-in" : ""}`}
          style={{ transitionDelay: `${delay + i * stagger}ms` }}
        >
          {w}
        </span>
      ))}
    </Tag>
  );
}

// ===== Reveal wrapper: fade+blur+slide on scroll =====
function Reveal({ children, delay = 0, y = 20, className = "", as = "div", once = true, id }) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const rect = ref.current.getBoundingClientRect();
    if (rect.top < window.innerHeight * 0.95 && rect.bottom > 0) {
      // Element už je v viewportu, ale počkej na preloader před spuštěním animace
      let preloaderCancelled = false;
      waitForPreloader(() => { if (!preloaderCancelled) setVisible(true); });
      return () => { preloaderCancelled = true; };
    }
    let fired = false;
    const fire = () => { if (!fired) { fired = true; setVisible(true); } };
    const fallback = setTimeout(fire, REVEAL_FALLBACK_MS);
    const obs = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          fire();
          clearTimeout(fallback);
          if (once) obs.disconnect();
        } else if (!once) {
          setVisible(false);
        }
      },
      { threshold: 0.12, rootMargin: "0px 0px -120px 0px" }
    );
    obs.observe(ref.current);
    return () => { obs.disconnect(); clearTimeout(fallback); };
  }, [once]);
  const Tag = as;
  return (
    <Tag
      ref={ref}
      id={id}
      className={`reveal ${visible ? "is-in" : ""} ${className}`}
      style={{ transitionDelay: `${delay}ms`, "--reveal-y": `${y}px` }}
    >
      {children}
    </Tag>
  );
}

// ===== Stagger group: child .stagger-item appear in sequence =====
function StaggerGroup({ children, step = 80, delay = 0, className = "", once = true, contents = false, reverse = false }) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    // display:contents wrapper nemá layout box → observer/getBoundingClientRect
    // nefungují korektně. Pro contents=true použijeme první rendered dítě.
    const target = contents
      ? (ref.current.firstElementChild || ref.current)
      : ref.current;
    const rect = target.getBoundingClientRect();
    if (rect.top < window.innerHeight * 0.95 && rect.bottom > 0) {
      // Element už je v viewportu, ale počkej na preloader před spuštěním animace
      let preloaderCancelled = false;
      waitForPreloader(() => { if (!preloaderCancelled) setVisible(true); });
      return () => { preloaderCancelled = true; };
    }
    let fired = false;
    const fire = () => { if (!fired) { fired = true; setVisible(true); } };
    const fallback = setTimeout(fire, REVEAL_FALLBACK_MS);
    const obs = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          fire();
          clearTimeout(fallback);
          if (once) obs.disconnect();
        }
      },
      { threshold: 0.1, rootMargin: "0px 0px -60px 0px" }
    );
    obs.observe(target);
    return () => { obs.disconnect(); clearTimeout(fallback); };
  }, [once, contents]);

  const arr = React.Children.toArray(children);
  const N = arr.length;
  return (
    <div ref={ref} className={`stagger ${visible ? "is-in" : ""} ${className}`} style={contents ? { display: "contents" } : undefined}>
      {arr.map((child, i) => {
        const order = reverse ? (N - 1 - i) : i;
        return React.cloneElement(child, {
          style: {
            ...(child.props.style || {}),
            transitionDelay: `${delay + order * step}ms`,
          },
          className: `stagger-item ${child.props.className || ""}`,
        });
      })}
    </div>
  );
}

// ===== RevealSlide: cinematic slide-in z libovolného směru (left/right/up/down) =====
function RevealSlide({ children, delay = 0, dir = "left", distance = 120, rotate = 0, className = "", as = "div", once = true, id }) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const rect = ref.current.getBoundingClientRect();
    if (rect.top < window.innerHeight * 0.95 && rect.bottom > 0) {
      // Element už je v viewportu, ale počkej na preloader před spuštěním animace
      let preloaderCancelled = false;
      waitForPreloader(() => { if (!preloaderCancelled) setVisible(true); });
      return () => { preloaderCancelled = true; };
    }
    let fired = false;
    const fire = () => { if (!fired) { fired = true; setVisible(true); } };
    const fallback = setTimeout(fire, REVEAL_FALLBACK_MS);
    const obs = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          fire();
          clearTimeout(fallback);
          if (once) obs.disconnect();
        }
      },
      { threshold: 0.15, rootMargin: "0px 0px -80px 0px" }
    );
    obs.observe(ref.current);
    return () => { obs.disconnect(); clearTimeout(fallback); };
  }, [once]);
  const Tag = as;
  let x = 0, y = 0;
  if (dir === "right") x = distance;
  else if (dir === "left") x = -distance;
  else if (dir === "up") y = distance;
  else if (dir === "down") y = -distance;
  return (
    <Tag
      ref={ref}
      id={id}
      className={`reveal-slide ${visible ? "is-in" : ""} ${className}`}
      style={{
        transitionDelay: `${delay}ms`,
        "--slide-x": `${x}px`,
        "--slide-y": `${y}px`,
        "--slide-rot": `${rotate}deg`
      }}
    >
      {children}
    </Tag>
  );
}

window.BlurText = BlurText;
window.Reveal = Reveal;
window.StaggerGroup = StaggerGroup;
window.RevealSlide = RevealSlide;
