/* Celestia Pictures — primitives & helpers */

const { useState, useEffect, useRef, useMemo } = React;

/* Intersection-observer based reveal — also watches the DOM
   for new .reveal elements added later (e.g. when project
   filters change). */
function useReveal() {
  useEffect(() => {
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      });
    }, { rootMargin: '-8% 0px -8% 0px', threshold: 0.05 });

    const observe = (root) => {
      root.querySelectorAll('.reveal:not(.in), .hr-anim:not(.in)').forEach((el) => io.observe(el));
    };
    observe(document);

    // Watch for new nodes added by React re-renders (filters, tabs, etc.)
    const mo = new MutationObserver((muts) => {
      muts.forEach((m) => {
        m.addedNodes.forEach((n) => {
          if (!(n instanceof Element)) return;
          if (n.matches && n.matches('.reveal, .hr-anim')) io.observe(n);
          observe(n);
        });
      });
    });
    mo.observe(document.body, { childList: true, subtree: true });

    // Safety net: after 2.5s, force any still-hidden reveal elements
    // visible — protects against any IO race / dropped entries.
    const safety = setTimeout(() => {
      document.querySelectorAll('.reveal:not(.in), .hr-anim:not(.in)').forEach((el) => {
        el.classList.add('in');
      });
    }, 2500);

    return () => { io.disconnect(); mo.disconnect(); clearTimeout(safety); };
  }, []);
}

/* Scroll spy + scrolled state */
function useNav(sections) {
  const [scrolled, setScrolled] = useState(false);
  const [active, setActive] = useState(sections[0]);
  useEffect(() => {
    const onScroll = () => {
      setScrolled(window.scrollY > 24);
      // find current section
      const y = window.scrollY + window.innerHeight * 0.35;
      let cur = sections[0];
      for (const id of sections) {
        const el = document.getElementById(id);
        if (el && el.offsetTop <= y) cur = id;
      }
      setActive(cur);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, [sections]);
  return { scrolled, active };
}

/* Small Star field for hero */
function StarField({ count = 60 }) {
  const stars = useMemo(() => {
    return Array.from({ length: count }, (_, i) => ({
      key: i,
      top: Math.random() * 100,
      left: Math.random() * 100,
      size: Math.random() * 2 + 0.7,
      dur: 3 + Math.random() * 5,
      delay: Math.random() * 5,
    }));
  }, [count]);
  return (
    <div className="hero-stars" aria-hidden="true">
      {stars.map(s => (
        <span
          key={s.key}
          className="star"
          style={{
            top: `${s.top}%`,
            left: `${s.left}%`,
            width: `${s.size}px`,
            height: `${s.size}px`,
            '--dur': `${s.dur}s`,
            '--delay': `${s.delay}s`,
          }}
        />
      ))}
    </div>
  );
}

/* Cinematic placeholder visual */
function CinematicVisual({ variant = 1, glyph }) {
  return (
    <div className={`proj-visual cv v${variant}`}>
      <div className="glyph">{glyph}</div>
      <div className="scan" />
      <div className="vignette" />
    </div>
  );
}

/* Smooth scroll helper */
function scrollToSection(id) {
  const el = document.getElementById(id);
  if (el) {
    const top = el.getBoundingClientRect().top + window.scrollY - 60;
    window.scrollTo({ top, behavior: 'smooth' });
  }
}

Object.assign(window, {
  useReveal, useNav, StarField, CinematicVisual, scrollToSection,
});
