/* Shared helpers for the ALEROQ landing UI kit.
   Ic = Lucide icon renderer. Exposed on window for the other babel scripts. */
function Ic({ name, size = 24, stroke = 1.6, color }) {
  const ref = React.useCallback((node) => {
    if (node && window.lucide && lucide[name]) {
      const el = lucide.createElement(lucide[name]);
      el.setAttribute('width', size);
      el.setAttribute('height', size);
      el.setAttribute('stroke-width', stroke);
      if (color) el.setAttribute('stroke', color);
      node.innerHTML = '';
      node.appendChild(el);
    }
  }, [name, size, stroke, color]);
  return <span ref={ref} style={{ display: 'inline-flex', lineHeight: 0 }} />;
}

/* Circuit-board pattern as a CSS background data-URI — faint, low opacity overlay. */
const CIRCUIT_SVG = encodeURIComponent(`
<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160' viewBox='0 0 160 160'>
  <g fill='none' stroke='%23ffffff' stroke-width='1'>
    <path d='M0 40 H50 M50 40 V90 M50 90 H120 M120 90 V20 M120 20 H160'/>
    <path d='M0 120 H30 M30 120 V70 M30 70 H80 M80 70 V140 M80 140 H160'/>
    <path d='M110 160 V120 M110 120 H150 M150 120 V80'/>
  </g>
  <g fill='%23ffffff'>
    <circle cx='50' cy='40' r='3'/><circle cx='50' cy='90' r='3'/>
    <circle cx='120' cy='90' r='3'/><circle cx='120' cy='20' r='3'/>
    <circle cx='30' cy='120' r='3'/><circle cx='30' cy='70' r='3'/>
    <circle cx='80' cy='70' r='3'/><circle cx='80' cy='140' r='3'/>
    <circle cx='110' cy='120' r='3'/><circle cx='150' cy='80' r='3'/>
  </g>
</svg>`).replace(/%23/g, '%23');

function aleroqScrollTo(id) {
  const el = document.getElementById(id);
  if (!el) return;
  const y = el.getBoundingClientRect().top + window.scrollY - 56;
  window.scrollTo({ top: y, behavior: 'smooth' });
}

/* Reveal — fade + rise into view on scroll. Robust: IntersectionObserver +
   scroll/resize fallback + safety timeout so content is never stuck hidden. */
function Reveal({ children, delay = 0, y = 26, dir = 'up', as = 'div', style = {}, ...rest }) {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    const node = ref.current;
    if (!node) return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      setShown(true); return;
    }
    let done = false;
    const reveal = () => { if (!done) { done = true; setShown(true); } };
    const check = () => {
      const r = node.getBoundingClientRect();
      if (r.top < (window.innerHeight || 800) * 0.92 && r.bottom > 0) reveal();
    };
    check();
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) reveal(); });
    }, { threshold: 0.12, rootMargin: '0px 0px -6% 0px' });
    io.observe(node);
    window.addEventListener('scroll', check, { passive: true });
    window.addEventListener('resize', check);
    // Position-aware safety net: re-probe periodically in case a scroll event
    // is missed. Never force-reveals off-screen content (check() gates on rect).
    const poll = setInterval(() => { if (done) { clearInterval(poll); return; } check(); }, 500);
    return () => {
      io.disconnect();
      window.removeEventListener('scroll', check);
      window.removeEventListener('resize', check);
      clearInterval(poll);
    };
  }, []);
  const Tag = as;
  const hidden = {
    up: `translateY(${y}px)`,
    down: `translateY(-${y}px)`,
    left: `translateX(${y + 16}px)`,
    right: `translateX(-${y + 16}px)`,
    scale: 'scale(0.9)',
  }[dir] || `translateY(${y}px)`;
  return (
    <Tag ref={ref} style={{
      opacity: shown ? 1 : 0,
      transform: shown ? 'none' : hidden,
      filter: shown ? 'none' : 'blur(6px)',
      transition: `opacity 0.85s var(--ease-out) ${delay}ms, transform 0.95s var(--ease-out) ${delay}ms, filter 0.85s var(--ease-out) ${delay}ms`,
      willChange: 'opacity, transform',
      ...style,
    }} {...rest}>{children}</Tag>
  );
}

/* useCountUp — animate a number from 0 to `target` once `active` flips true. */
function useCountUp(target, active, duration = 1300) {
  const [val, setVal] = React.useState(0);
  React.useEffect(() => {
    if (!active) return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      setVal(target); return;
    }
    let raf, start;
    const tick = (t) => {
      if (start == null) start = t;
      const p = Math.min((t - start) / duration, 1);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(Math.round(target * eased));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, active, duration]);
  return val;
}

/* useTheme — current page theme ('light' | 'dark'), reactive. Nav's toggle
   dispatches 'aleroq-themechange' on window; any component that branches on
   theme (PostFX, etc.) subscribes here and re-renders. */
function useTheme() {
  const read = () => document.documentElement.getAttribute('data-theme') === 'light' ? 'light' : 'dark';
  const [theme, setTheme] = React.useState(read);
  React.useEffect(() => {
    const onChange = () => setTheme(read());
    window.addEventListener('aleroq-themechange', onChange);
    return () => window.removeEventListener('aleroq-themechange', onChange);
  }, []);
  return theme;
}
window.useTheme = useTheme;

window.Ic = Ic;
window.Reveal = Reveal;
window.useCountUp = useCountUp;
window.aleroqScrollTo = aleroqScrollTo;
window.ALEROQ_CIRCUIT = `url("data:image/svg+xml,${CIRCUIT_SVG}")`;

/* Film grain — shared across every photo so the whole page reads as one film. */
const GRAIN_SVG = encodeURIComponent(
  "<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'>" +
  "<filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter>" +
  "<rect width='100%' height='100%' filter='url(#n)'/></svg>"
);
window.ALEROQ_GRAIN = `url("data:image/svg+xml,${GRAIN_SVG}")`;

/* PhotoSlot — a user-fillable <image-slot> under a fixed cinematic grade:
   darkening gradient + vignette + brand tint + grain. Overlays are
   pointer-events:none so the slot stays droppable. `tint` shifts the brand
   wash (red = problem, green = resolution) to carry the arc across photos. */
function PhotoSlot({ id, placeholder, tint = 'neutral', radius = 0, darken = 0.5, style = {}, children }) {
  const tints = {
    neutral: 'linear-gradient(180deg, rgba(8,8,9,0.25), rgba(8,8,9,0.55))',
    red: 'linear-gradient(180deg, rgba(229,25,42,0.18), rgba(8,8,9,0.55) 70%)',
    green: 'linear-gradient(180deg, rgba(8,8,9,0.4), rgba(16,180,110,0.20) 100%)',
  };
  const ov = { position: 'absolute', inset: 0, pointerEvents: 'none' };
  return (
    <div style={{ position: 'relative', overflow: 'hidden', borderRadius: radius, background: '#070708', ...style }}>
      {React.createElement('image-slot', {
        id, shape: 'rect', placeholder,
        style: { position: 'absolute', inset: 0, width: '100%', height: '100%' },
      })}
      <div style={{ ...ov, background: tints[tint], mixBlendMode: 'multiply' }} />
      <div style={{ ...ov, background: `radial-gradient(120% 95% at 50% 38%, transparent 42%, rgba(4,4,5,${0.55 + darken * 0.5}) 100%)` }} />
      <div style={{ ...ov, backgroundImage: window.ALEROQ_GRAIN, backgroundSize: '180px 180px', opacity: 0.10, mixBlendMode: 'overlay' }} />
      {children}
    </div>
  );
}
window.PhotoSlot = PhotoSlot;

/* CinematicPhoto — a real photo under the shared film grade (tint + vignette +
   grain), optionally with a slow Ken Burns push. Baked imagery for the page. */
function CinematicPhoto({ src, tint = 'neutral', kenburns = false, darken = 0.5, radius = 0, style = {}, children }) {
  const tints = {
    neutral: 'linear-gradient(180deg, rgba(8,8,9,0.30), rgba(8,8,9,0.62))',
    red: 'linear-gradient(180deg, rgba(229,25,42,0.22), rgba(8,8,9,0.62) 72%)',
    green: 'linear-gradient(180deg, rgba(8,8,9,0.45), rgba(16,180,110,0.24) 100%)',
  };
  const ov = { position: 'absolute', inset: 0, pointerEvents: 'none' };
  return (
    <div style={{ position: 'relative', overflow: 'hidden', borderRadius: radius, background: '#070708', ...style }}>
      <div className={kenburns ? 'kenburns' : ''} style={{
        position: 'absolute', inset: 0,
        backgroundImage: `url("${src}")`, backgroundSize: 'cover', backgroundPosition: 'center',
      }} />
      <div style={{ ...ov, background: tints[tint], mixBlendMode: 'multiply' }} />
      <div style={{ ...ov, background: `radial-gradient(125% 100% at 50% 36%, transparent 40%, rgba(4,4,5,${0.5 + darken * 0.5}) 100%)` }} />
      <div style={{ ...ov, backgroundImage: window.ALEROQ_GRAIN, backgroundSize: '180px 180px', opacity: 0.09, mixBlendMode: 'overlay' }} />
      {children}
    </div>
  );
}
window.CinematicPhoto = CinematicPhoto;

/* Curated real photography (Unsplash CDN) — dark, cinematic, automotive. */
window.ALEROQ_IMG = {
  hero: 'https://images.unsplash.com/photo-1503376780353-7e6692767b70?auto=format&fit=crop&w=2400&q=80',
  problem: 'https://images.unsplash.com/photo-1486262715619-67b85e0b08d3?auto=format&fit=crop&w=1600&q=80',
  waitlist: 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&w=2400&q=80',
};

const PREFERS_REDUCED = () => window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

/* Shared scroll scheduler — ONE scroll/resize listener and ONE rAF for the
   whole page. Every parallax/scroll effect subscribes here instead of adding
   its own listener+rAF, which collapses N layout reads per scroll into a single
   batched pass and kills the jank. Subscribers get (scrollY, viewportH). */
const ALEROQScroll = (() => {
  const subs = new Set();
  let ticking = false;
  const tick = () => {
    ticking = false;
    const sy = window.scrollY || window.pageYOffset || 0;
    const vh = window.innerHeight || 800;
    subs.forEach((fn) => { try { fn(sy, vh); } catch (e) { /* keep others alive */ } });
  };
  const request = () => { if (!ticking) { ticking = true; requestAnimationFrame(tick); } };
  window.addEventListener('scroll', request, { passive: true });
  window.addEventListener('resize', request);
  return {
    subscribe(fn) { subs.add(fn); request(); return () => subs.delete(fn); },
    request,
  };
})();
window.ALEROQScroll = ALEROQScroll;

/* Parallax — translates its content relative to viewport center as you scroll.
   speed>0 moves with scroll (slower than page = depth); negative moves against.
   Driven by the shared scheduler; skips work while fully off-screen. */
function Parallax({ speed = 0.15, axis = 'y', children, style = {}, ...rest }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const node = ref.current;
    if (!node || PREFERS_REDUCED()) return;
    const update = (sy, vh) => {
      const r = node.getBoundingClientRect();
      if (r.bottom < -120 || r.top > vh + 120) return; // cull off-screen
      const off = (r.top + r.height / 2) - vh / 2;
      const t = -off * speed;
      node.style.transform = axis === 'y'
        ? `translate3d(0, ${t.toFixed(1)}px, 0)`
        : `translate3d(${t.toFixed(1)}px, 0, 0)`;
    };
    return window.ALEROQScroll.subscribe(update);
  }, [speed, axis]);
  return <div ref={ref} style={{ willChange: 'transform', ...style }} {...rest}>{children}</div>;
}

/* CursorGlow — a soft red glow that follows the pointer. Desktop only. */
function CursorGlow() {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (window.matchMedia && window.matchMedia('(hover: none)').matches) { el.style.display = 'none'; return; }
    let raf = 0, x = window.innerWidth / 2, y = window.innerHeight / 2;
    const move = (e) => {
      x = e.clientX; y = e.clientY;
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => { el.style.transform = `translate(${x - 250}px, ${y - 250}px)`; });
    };
    window.addEventListener('pointermove', move);
    return () => { window.removeEventListener('pointermove', move); cancelAnimationFrame(raf); };
  }, []);
  return <div ref={ref} style={{
    position: 'fixed', top: 0, left: 0, width: 500, height: 500, borderRadius: '50%',
    background: 'radial-gradient(closest-side, rgba(255,46,63,0.16), rgba(255,46,63,0.05) 45%, transparent 70%)',
    pointerEvents: 'none', zIndex: 60, mixBlendMode: 'screen', filter: 'blur(4px)',
    transform: 'translate(-9999px,-9999px)',
  }} />;
}

/* Particles — faint embers drifting upward. Decorative ambient depth. */
function Particles({ count = 16 }) {
  const dots = React.useMemo(() => Array.from({ length: count }, (_, i) => ({
    left: Math.random() * 100, size: 1 + Math.random() * 2.5,
    dur: 9 + Math.random() * 12, delay: -Math.random() * 16,
    drift: (Math.random() - 0.5) * 60, op: 0.15 + Math.random() * 0.4,
  })), [count]);
  if (PREFERS_REDUCED()) return null;
  return (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', pointerEvents: 'none' }}>
      {dots.map((d, i) => (
        <span key={i} className="ember" style={{
          position: 'absolute', bottom: -10, left: `${d.left}%`,
          width: d.size, height: d.size, borderRadius: '50%',
          background: 'rgba(255,46,63,0.9)', boxShadow: '0 0 8px rgba(255,46,63,0.8)',
          opacity: d.op, '--drift': `${d.drift}px`,
          animation: `emberRise ${d.dur}s linear ${d.delay}s infinite`,
        }} />
      ))}
    </div>
  );
}

Object.assign(window, { Parallax, CursorGlow, Particles });

/* Magnetic — child (usually a button) subtly pulls toward the cursor on
   approach, then springs back. Desktop only; respects reduced motion. */
function Magnetic({ strength = 0.4, children, style = {} }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    if (PREFERS_REDUCED()) return;
    if (window.matchMedia && window.matchMedia('(hover: none)').matches) return;
    let raf = 0;
    const move = (e) => {
      const r = el.getBoundingClientRect();
      const mx = e.clientX - (r.left + r.width / 2);
      const my = e.clientY - (r.top + r.height / 2);
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.transform = `translate(${(mx * strength).toFixed(1)}px, ${(my * strength).toFixed(1)}px)`;
      });
    };
    const leave = () => { cancelAnimationFrame(raf); el.style.transform = 'translate(0,0)'; };
    const zone = el.parentElement || el;
    zone.addEventListener('pointermove', move);
    zone.addEventListener('pointerleave', leave);
    return () => { zone.removeEventListener('pointermove', move); zone.removeEventListener('pointerleave', leave); cancelAnimationFrame(raf); };
  }, [strength]);
  return <span ref={ref} style={{ display: 'inline-flex', transition: 'transform 0.35s var(--ease-out)', willChange: 'transform', ...style }}>{children}</span>;
}
window.Magnetic = Magnetic;

/* PostFX — cinematic full-page after-effects overlay:
   • scroll-progress bar that grades red -> green as you descend
   • soft vignette framing the whole viewport
   • faint film grain for filmic texture
   State-driven (no transition dependency) so it always renders. */
function PostFX() {
  const barRef = React.useRef(null);
  const theme = window.useTheme();
  const light = theme === 'light';
  React.useEffect(() => {
    const update = (sy) => {
      const bar = barRef.current; if (!bar) return;
      const h = document.documentElement.scrollHeight - window.innerHeight;
      const p = h > 0 ? Math.min(sy / h, 1) : 0;
      const lead = `hsl(${Math.round(p * 150)}, 85%, 55%)`;
      bar.style.width = `${(p * 100).toFixed(2)}%`;
      bar.style.background = `linear-gradient(90deg, #FF2E3F, ${lead})`;
      bar.style.boxShadow = `0 0 12px ${lead}`;
    };
    return window.ALEROQScroll.subscribe(update);
  }, []);
  return (
    <React.Fragment>
      {/* scroll progress */}
      <div style={{ position: 'fixed', top: 0, left: 0, right: 0, height: 3, zIndex: 70, pointerEvents: 'none', background: light ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.05)' }}>
        <div ref={barRef} style={{
          height: '100%', width: '0%',
          background: 'linear-gradient(90deg, #FF2E3F, #FF2E3F)',
          boxShadow: '0 0 12px #FF2E3F',
        }} />
      </div>
      {/* cinematic vignette — far gentler in light mode so it doesn't muddy the page */}
      <div style={{
        position: 'fixed', inset: 0, zIndex: 55, pointerEvents: 'none',
        boxShadow: light ? 'inset 0 0 160px 30px rgba(0,0,0,0.06)' : 'inset 0 0 200px 40px rgba(0,0,0,0.55)',
        background: light
          ? 'radial-gradient(125% 110% at 50% 50%, transparent 70%, rgba(0,0,0,0.04) 100%)'
          : 'radial-gradient(125% 110% at 50% 50%, transparent 58%, rgba(0,0,0,0.4) 100%)',
      }} />
      {/* film grain */}
      <div className="grain-fx" style={{
        position: 'fixed', inset: '-50%', zIndex: 56, pointerEvents: 'none',
        backgroundImage: window.ALEROQ_GRAIN, backgroundSize: '170px 170px',
        opacity: light ? 0.02 : 0.05, mixBlendMode: light ? 'multiply' : 'overlay',
      }} />
    </React.Fragment>
  );
}
window.PostFX = PostFX;
