// Enhancements: extra animations layered onto the agYAP landing page.
// Components here are mounted via portals from App, plus a few helpers other
// parts of the page consume directly.

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

// ── Scroll progress bar (top stripe) ──────────────────────────────
function ScrollProgress() {
  const [w, setW] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement;
      const max = h.scrollHeight - h.clientHeight;
      setW(max > 0 ? (h.scrollTop / max) * 100 : 0);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);
  return (
    <div className="scroll-progress" aria-hidden="true">
      <i style={{ width: `${w}%` }} />
    </div>
  );
}

// ── Static SVG grain overlay ─────────────────────────────────────
function GrainOverlay() {
  return (
    <svg className="grain-overlay" aria-hidden="true">
      <filter id="grainfilter">
        <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed="7" />
        <feColorMatrix values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.6 0" />
      </filter>
      <rect width="100%" height="100%" filter="url(#grainfilter)" />
    </svg>
  );
}

// ── Hero metaball canvas (chromatic-aberration tint) ─────────────
function HeroBlobs() {
  const ref = useRef(null);
  const mouse = useRef({ x: 0.5, y: 0.5 });
  useEffect(() => {
    const canvas = ref.current;
    if (!canvas) return undefined;
    const ctx = canvas.getContext('2d');
    let raf, w, h, dpr;
    const blobs = Array.from({ length: 6 }).map((_, i) => ({
      x: Math.random(), y: Math.random(),
      vx: (Math.random() - 0.5) * 0.0006,
      vy: (Math.random() - 0.5) * 0.0006,
      r: 0.18 + Math.random() * 0.12,
      hue: i,
    }));
    const colors = ['#A78BFA', '#FF5436', '#9DC8FF', '#A78BFA', '#FF8A5C', '#B19BFF'];
    const resize = () => {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = canvas.clientWidth; h = canvas.clientHeight;
      canvas.width = w * dpr; canvas.height = h * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    window.addEventListener('resize', resize);
    const onMove = (e) => {
      const r = canvas.getBoundingClientRect();
      mouse.current.x = (e.clientX - r.left) / r.width;
      mouse.current.y = (e.clientY - r.top) / r.height;
    };
    window.addEventListener('mousemove', onMove);
    let lastT = performance.now();
    const tick = (t) => {
      // delta-time normalised to 60fps frames so motion is frame-rate-independent
      const dt = Math.min(3, Math.max(0.1, (t - lastT) / 16.667));
      lastT = t;
      ctx.clearRect(0, 0, w, h);
      ctx.globalCompositeOperation = 'lighter';
      blobs.forEach((b, i) => {
        // drift toward cursor mildly, with damping so velocity can't accumulate
        b.vx += (mouse.current.x - b.x) * 0.000003 * dt;
        b.vy += (mouse.current.y - b.y) * 0.000003 * dt;
        b.vx *= Math.pow(0.985, dt);
        b.vy *= Math.pow(0.985, dt);
        // clamp velocity magnitude so it can never run away
        const vmax = 0.0015;
        if (b.vx > vmax) b.vx = vmax; else if (b.vx < -vmax) b.vx = -vmax;
        if (b.vy > vmax) b.vy = vmax; else if (b.vy < -vmax) b.vy = -vmax;
        b.x += (b.vx + Math.sin(t / 4000 + i) * 0.0006) * dt;
        b.y += (b.vy + Math.cos(t / 5200 + i * 1.7) * 0.0006) * dt;
        if (b.x < -0.2) b.x = 1.2; if (b.x > 1.2) b.x = -0.2;
        if (b.y < -0.2) b.y = 1.2; if (b.y > 1.2) b.y = -0.2;
        const cx = b.x * w, cy = b.y * h, rad = b.r * Math.min(w, h);
        // chromatic-aberration: 3 offset radial gradients
        const offsets = [[-3, 0, '#FF5436'], [3, 0, '#9DC8FF'], [0, 0, colors[i]]];
        offsets.forEach(([dx, dy, col]) => {
          const g = ctx.createRadialGradient(cx + dx, cy + dy, 0, cx + dx, cy + dy, rad);
          g.addColorStop(0, col + '55');
          g.addColorStop(1, col + '00');
          ctx.fillStyle = g;
          ctx.beginPath();
          ctx.arc(cx + dx, cy + dy, rad, 0, Math.PI * 2);
          ctx.fill();
        });
      });
      ctx.globalCompositeOperation = 'source-over';
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); window.removeEventListener('mousemove', onMove); };
  }, []);
  return <canvas ref={ref} className="hero-blobs" aria-hidden="true" />;
}

// ── Reveal-on-scroll: tag elements; they fade+rise in stagger ────
function useRevealOnScroll(deps = []) {
  useEffect(() => {
    const targets = document.querySelectorAll('[data-reveal]');
    if (!('IntersectionObserver' in window)) {
      targets.forEach((el) => el.classList.add('is-revealed'));
      return undefined;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((en) => {
        if (en.isIntersecting) {
          const items = en.target.querySelectorAll('[data-reveal-child]');
          if (items.length) {
            items.forEach((el, i) => setTimeout(() => el.classList.add('is-revealed'), i * 60));
          }
          en.target.classList.add('is-revealed');
          io.unobserve(en.target);
        }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
    targets.forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, deps);
}

// ── Live latency readout ─────────────────────────────────────────
function LatencyReadout({ base = 142, jitter = 22, prefix = 'LATENCY' }) {
  const [v, setV] = useState(base);
  useEffect(() => {
    const id = setInterval(() => {
      setV(Math.round(base + (Math.random() - 0.5) * jitter * 2));
    }, 2000);
    return () => clearInterval(id);
  }, [base, jitter]);
  return <span className="mono">{prefix} {v}MS</span>;
}

// ── Pipelines visualizer (STT/LLM/TTS/VAD with packets) ──────────
function PipelineVis() {
  const stages = ['STT', 'LLM', 'TTS', 'VAD'];
  return (
    <div className="vis-pipeline">
      <div className="vp-stages">
        {stages.map((s, i) => (
          <i key={s} style={{ animationDelay: `${i * 0.25}s` }}>{s}</i>
        ))}
      </div>
      <div className="vp-track">
        {[0, 1, 2].map((k) => (
          <span key={k} className="vp-packet" style={{ animationDelay: `${k * 0.33}s` }} />
        ))}
      </div>
    </div>
  );
}

// ── Push notification mock (slides in every 2.4s) ─────────────────
function PushVis() {
  const [tick, setTick] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setTick((t) => t + 1), 2400);
    return () => clearInterval(id);
  }, []);
  return (
    <div className="vis-push" key={tick}>
      <div className="vp-pill">
        <span className="vp-dot" />
        <div>
          <div className="vp-app">agYAP · now</div>
          <div className="vp-msg">Nova · Migration done. 142 rows.</div>
        </div>
      </div>
    </div>
  );
}

// ── Discovery radar with sweep beam + sequenced rings ─────────────
function DiscoveryRadar() {
  return (
    <div className="vis-radar2">
      <div className="vr-beam" />
      <div className="vr-rings">
        <div /><div /><div />
      </div>
      <div className="vr-blip vr-b1" />
      <div className="vr-blip vr-b2" />
      <div className="vr-blip vr-b3" />
    </div>
  );
}

// ── Avatar cycle (switch agents mid-sentence) ────────────────────
function AvatarCycle() {
  const tiles = [
    { i: 'NV', c: '#A78BFA' },
    { i: 'KT', c: '#FF5436' },
    { i: 'OS', c: '#9DC8FF' },
    { i: 'MR', c: '#E0BBFF' },
  ];
  const [active, setActive] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setActive((a) => (a + 1) % tiles.length), 1400);
    return () => clearInterval(id);
  }, []);
  return (
    <div className="vis-avatars">
      {tiles.map((t, i) => (
        <span key={i} className="va-tile" data-on={i === active ? '1' : '0'} style={{ '--c': t.c }}>
          {t.i}
        </span>
      ))}
    </div>
  );
}

// ── Sub-200ms voice loop visualizer (canvas wave + ghost) ─────────
function VoiceLoopVis() {
  const ref = useRef(null);
  useEffect(() => {
    const canvas = ref.current; if (!canvas) return undefined;
    const ctx = canvas.getContext('2d');
    let raf, w, h, dpr;
    const resize = () => {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = canvas.clientWidth; h = canvas.clientHeight;
      canvas.width = w * dpr; canvas.height = h * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const onR = () => resize(); window.addEventListener('resize', onR);
    const tick = (t) => {
      ctx.clearRect(0, 0, w, h);
      // ghost trail
      const drawWave = (off, alpha, weight) => {
        ctx.beginPath();
        for (let x = 0; x <= w; x += 2) {
          const vad = 0.5 + 0.5 * Math.sin(t / 800);
          const y = h / 2 + Math.sin(x / 20 + t / 200 + off) * 14 * vad
                    + Math.sin(x / 7 + t / 90) * 4 * vad
                    + (Math.random() - 0.5) * 1.2 * vad;
          if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
        }
        ctx.strokeStyle = `rgba(14,14,12,${alpha})`;
        ctx.lineWidth = weight;
        ctx.stroke();
      };
      drawWave(0.6, 0.18, 1);
      drawWave(0.3, 0.32, 1);
      drawWave(0, 0.95, 1.5);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', onR); };
  }, []);
  return <canvas ref={ref} className="vis-voiceloop" aria-hidden="true" />;
}

// ── Typewriter words: type → hold → backspace → next word, repeat ─────
// (Component name kept as ParticleWords so app.jsx callsites don't change.)
function ParticleWords({ words }) {
  const [display, setDisplay] = useState('');
  useEffect(() => {
    let cancelled = false;
    const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
    const TYPE_MS = 55;          // fast type
    const ERASE_MS = 32;         // faster backspace
    const PAUSE_MS = 180;        // brief pause after erase
    const holdFor = (w) => (w === 'agYAP' ? 6000 : 2000);

    async function run() {
      let i = 0;
      // initial pause so the LetterReveal in front has time to land
      await sleep(400);
      while (!cancelled) {
        const word = words[i] + '.';
        for (let n = 1; n <= word.length; n++) {
          if (cancelled) return;
          setDisplay(word.slice(0, n));
          await sleep(TYPE_MS);
        }
        await sleep(holdFor(words[i]));
        if (cancelled) return;
        for (let n = word.length - 1; n >= 0; n--) {
          if (cancelled) return;
          setDisplay(word.slice(0, n));
          await sleep(ERASE_MS);
        }
        await sleep(PAUSE_MS);
        i = (i + 1) % words.length;
      }
    }
    run();
    return () => { cancelled = true; };
  }, [words.join('|')]);

  // Stack a hidden ghost for every word — the inline-grid cell sizes to the
  // widest one (italic capitals can be wider than longer lowercase strings,
  // so character count alone isn't enough to pick the widest).
  return (
    <span className="particle-words" aria-label={words.join(' ')}>
      {words.map((w, i) => (
        <span key={i} className="tw-ghost" aria-hidden="true">{w}.</span>
      ))}
      <span className="tw-text">
        {display}
        <span className="tw-cursor" aria-hidden="true" />
      </span>
    </span>
  );
}

// ── Per-letter reveal on viewport enter ──────────────────────────
function LetterReveal({ text, className = '', stagger = 30 }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current; if (!el) return undefined;
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => {
        if (e.isIntersecting) {
          el.classList.add('lr-on');
          io.unobserve(el);
        }
      });
    }, { threshold: 0.3 });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  const chars = Array.from(text);
  return (
    <span ref={ref} className={`letter-reveal ${className}`}>
      {chars.map((c, i) => (
        <span key={i} className="lr-c" style={{ transitionDelay: `${i * stagger}ms` }}>
          {c === ' ' ? '\u00A0' : c}
        </span>
      ))}
    </span>
  );
}

// ── Aurora plate (3 drifting radial gradients under blur) ────────
function AuroraPlate({ slow = false }) {
  return (
    <div className={`aurora-plate ${slow ? 'slow' : ''}`} aria-hidden="true">
      <span /><span /><span />
    </div>
  );
}

// ── 1px ink waveform ribbon (full width) ─────────────────────────
function RibbonWave() {
  const ref = useRef(null);
  useEffect(() => {
    const canvas = ref.current; if (!canvas) return undefined;
    const ctx = canvas.getContext('2d');
    let raf, w, h, dpr;
    const resize = () => {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = canvas.clientWidth; h = canvas.clientHeight;
      canvas.width = w * dpr; canvas.height = h * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const onR = () => resize(); window.addEventListener('resize', onR);
    const tick = (t) => {
      ctx.clearRect(0, 0, w, h);
      ctx.beginPath();
      for (let x = 0; x <= w; x += 2) {
        const y = h / 2
          + Math.sin(x / 28 + t / 700) * (h / 3)
          + Math.sin(x / 9 + t / 1900) * (h / 6);
        if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
      }
      ctx.strokeStyle = '#0E0E0C';
      ctx.lineWidth = 1;
      ctx.stroke();
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', onR); };
  }, []);
  return <canvas ref={ref} className="ribbon-wave" aria-hidden="true" />;
}

// ── Phone scanlines + tearing canvas (overlays demo phone) ───────
function ScanlinesCanvas() {
  const ref = useRef(null);
  useEffect(() => {
    const canvas = ref.current; if (!canvas) return undefined;
    const ctx = canvas.getContext('2d');
    let raf, w, h, dpr;
    const resize = () => {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = canvas.clientWidth; h = canvas.clientHeight;
      canvas.width = w * dpr; canvas.height = h * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const onR = () => resize(); window.addEventListener('resize', onR);
    const tick = () => {
      ctx.clearRect(0, 0, w, h);
      for (let y = 0; y < h; y += 4) {
        const tear = Math.random() < 0.05 ? (Math.random() - 0.5) * 60 : 0;
        ctx.fillStyle = 'rgba(242,239,230,0.06)';
        ctx.fillRect(tear, y, w, 1);
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', onR); };
  }, []);
  return <canvas ref={ref} className="phone-scanlines" aria-hidden="true" />;
}

// ── Phone ripple shader (rings + bottom waveform) ────────────────
function PhoneRipples() {
  return (
    <div className="phone-ripples" aria-hidden="true">
      <span className="pr-ring" /><span className="pr-ring d2" /><span className="pr-ring d3" />
      <span className="pr-ring d4" /><span className="pr-ring d5" />
      <span className="pr-ring ghost" />
      <span className="pr-ring ghost d3" />
    </div>
  );
}

// ── Spotlight tracker (sets --mx --my on hover targets) ──────────
function useSpotlight(selector) {
  useEffect(() => {
    const els = document.querySelectorAll(selector);
    const onMove = (e) => {
      els.forEach((el) => {
        const r = el.getBoundingClientRect();
        const x = ((e.clientX - r.left) / r.width) * 100;
        const y = ((e.clientY - r.top) / r.height) * 100;
        el.style.setProperty('--mx', `${x}%`);
        el.style.setProperty('--my', `${y}%`);
      });
    };
    window.addEventListener('mousemove', onMove);
    return () => window.removeEventListener('mousemove', onMove);
  }, [selector]);
}

// ── "Coming soon" modal — intercepts clicks on every CTA-style element ───
function NoYapModal() {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    const sel = '.brand, .pill, .btn-primary, .btn-ghost, .btn-tier, .play, footer a';
    const onClick = (e) => {
      const target = e.target.closest(sel);
      if (!target) return;
      if (target.closest('.ny-modal') || target.closest('.twk-panel')) return;
      e.preventDefault();
      e.stopPropagation();
      setOpen(true);
    };
    document.addEventListener('click', onClick, true);
    return () => document.removeEventListener('click', onClick, true);
  }, []);

  useEffect(() => {
    if (!open) return undefined;
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [open]);

  if (!open) return null;
  return (
    <div className="ny-backdrop" onClick={() => setOpen(false)} role="dialog" aria-modal="true">
      <div className="ny-modal" onClick={(e) => e.stopPropagation()}>
        <button className="ny-close" onClick={() => setOpen(false)} aria-label="Close">✕</button>
        <h1 className="ny-title">SORRY, but no yapping yet.</h1>
        <p className="ny-text">we are coming soon... stay tuned</p>
      </div>
    </div>
  );
}

Object.assign(window, {
  ScrollProgress, GrainOverlay, HeroBlobs, useRevealOnScroll,
  LatencyReadout, PipelineVis, PushVis, DiscoveryRadar,
  AvatarCycle, VoiceLoopVis, LetterReveal,
  AuroraPlate, RibbonWave, ScanlinesCanvas, PhoneRipples, useSpotlight,
  ParticleWords, NoYapModal,
});
