/* global React */
// StarField — two stacked canvases inside the hero:
//   .star-field-bg   — background stars, sits BEHIND tiles + content
//   .star-field-fg   — cursor arrow + constellation lines, sits ON TOP
// The split lets the cursor paint over the 3D carousel while the stars
// stay in the deep background.

const { useEffect, useRef } = React;

const STAR_COUNT       = 170;
const LINK_RADIUS      = 200;
const LINK_MAX         = 6;
const CURSOR_GLOW_R    = 14;

function StarField() {
  const bgRef    = useRef(null);
  const fgRef    = useRef(null);
  const mouseRef = useRef({ x: -1000, y: -1000, active: false });
  const starsRef = useRef([]);
  const rafRef   = useRef(0);

  useEffect(() => {
    const bg = bgRef.current;
    const fg = fgRef.current;
    if (!bg || !fg) return;
    const bgCtx = bg.getContext("2d");
    const fgCtx = fg.getContext("2d");
    const dpr = Math.min(window.devicePixelRatio || 1, 2);

    const seedStars = (w, h) => {
      const arr = [];
      for (let i = 0; i < STAR_COUNT; i++) {
        arr.push({
          x: Math.random() * w,
          y: Math.random() * h,
          // Mostly small pinpoint dots with a rare brighter one — matches
          // the sparse night-sky reference (no big halos, sharp specks).
          r: Math.random() < 0.15 ? 1.0 + Math.random() * 0.6
                                  : 0.4 + Math.random() * 0.5,
          a: 0.55 + Math.random() * 0.4,
          tw: 0.4 + Math.random() * 1.2,
          ph: Math.random() * Math.PI * 2,
          vx: (Math.random() - 0.5) * 0.04,
          vy: (Math.random() - 0.5) * 0.04,
        });
      }
      starsRef.current = arr;
    };

    const resize = () => {
      const rect = bg.getBoundingClientRect();
      for (const c of [bg, fg]) {
        c.width  = rect.width  * dpr;
        c.height = rect.height * dpr;
      }
      bgCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
      fgCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
      seedStars(rect.width, rect.height);
    };
    resize();

    const onMove = (e) => {
      const rect = bg.getBoundingClientRect();
      mouseRef.current.x = e.clientX - rect.left;
      mouseRef.current.y = e.clientY - rect.top;
      mouseRef.current.active = true;
    };
    const onLeave = () => { mouseRef.current.active = false; };

    // Touch handlers — on mobile, react to tap (touchstart) only, not
    // touchmove, so that scrolling the page doesn't move the constellation
    // around. Lines appear where the user taps and stay there briefly, then
    // fade out so they don't follow the scroll.
    let touchFadeTimer = null;
    const onTouch = (e) => {
      if (!e.touches || !e.touches.length) return;
      const t = e.touches[0];
      const rect = bg.getBoundingClientRect();
      mouseRef.current.x = t.clientX - rect.left;
      mouseRef.current.y = t.clientY - rect.top;
      mouseRef.current.active = true;
      mouseRef.current.touch = true;
      if (touchFadeTimer) clearTimeout(touchFadeTimer);
      touchFadeTimer = setTimeout(() => {
        mouseRef.current.active = false;
      }, 1600);
    };

    const host = bg.parentElement || window;
    host.addEventListener("mousemove", onMove);
    host.addEventListener("mouseleave", onLeave);
    // Listen only for touchstart — NOT touchmove. touchmove fires during
    // page scroll on mobile, which would cause the constellation to jump.
    host.addEventListener("touchstart", onTouch, { passive: true });
    window.addEventListener("resize", resize);

    const tick = (now) => {
      const w = bg.clientWidth, h = bg.clientHeight;
      bgCtx.clearRect(0, 0, w, h);
      fgCtx.clearRect(0, 0, w, h);

      const stars = starsRef.current;
      const m = mouseRef.current;

      // ── BG layer: stars (twinkle + drift) ───────────────────────────
      for (let i = 0; i < stars.length; i++) {
        const s = stars[i];
        s.x += s.vx; s.y += s.vy;
        if (s.x < -10) s.x = w + 10;
        if (s.x > w + 10) s.x = -10;
        if (s.y < -10) s.y = h + 10;
        if (s.y > h + 10) s.y = -10;

        // Subtle twinkle (less aggressive than before so stars sit still
        // most of the time, like the reference).
        const twinkle = 0.8 + Math.sin(now * 0.001 * s.tw + s.ph) * 0.2;
        const alpha = s.a * twinkle;
        const r = s.r;

        // Sharp pinpoint — no halo glow. Just a small bright dot.
        bgCtx.fillStyle = `rgba(255,255,255,${alpha})`;
        bgCtx.beginPath();
        bgCtx.arc(s.x, s.y, r, 0, Math.PI * 2);
        bgCtx.fill();
      }

      // ── FG layer: lines + cursor ────────────────────────────────────
      if (m.active) {
        const candidates = [];
        for (let i = 0; i < stars.length; i++) {
          const s = stars[i];
          const dx = s.x - m.x, dy = s.y - m.y;
          const d  = Math.sqrt(dx * dx + dy * dy);
          if (d < LINK_RADIUS) candidates.push({ s, d });
        }
        candidates.sort((a, b) => a.d - b.d);
        const links = candidates.slice(0, LINK_MAX);

        bgCtx.lineCap = "round";
        for (const { s, d } of links) {
          const t = 1 - d / LINK_RADIUS;
          bgCtx.strokeStyle = `rgba(255,255,255,${0.45 * t})`;
          bgCtx.lineWidth = 0.7 + t * 0.5;
          bgCtx.beginPath();
          bgCtx.moveTo(m.x, m.y);
          bgCtx.lineTo(s.x, s.y);
          bgCtx.stroke();
        }

        // Soft glow behind cursor tip — REMOVED. The <GlobalCursor /> arrow
        // has its own drop-shadow glow that works across the entire page.
        // The previous canvas glow clipped awkwardly at the hero boundary.

        // Classic arrow pointer — desktop only (skip on touch taps).
        // We no longer draw the arrow here because <GlobalCursor /> renders
        // a single DOM-based cursor that follows the mouse across the whole
        // page — including outside the hero. Drawing here too would show
        // two arrows on top of each other inside the hero.
        if (false && !m.touch) {
          fgCtx.save();
          fgCtx.translate(m.x, m.y);
          fgCtx.fillStyle = "rgba(255,255,255,1)";
          fgCtx.strokeStyle = "rgba(0,0,0,0.55)";
          fgCtx.lineWidth = 1;
          fgCtx.lineJoin = "round";
          fgCtx.beginPath();
          fgCtx.moveTo(0, 0);
          fgCtx.lineTo(0, 16);
          fgCtx.lineTo(4.2, 12.2);
          fgCtx.lineTo(7.2, 18.4);
          fgCtx.lineTo(10, 17.2);
          fgCtx.lineTo(7, 11);
          fgCtx.lineTo(12.4, 11);
          fgCtx.closePath();
          fgCtx.fill();
          fgCtx.stroke();
          fgCtx.restore();
        }
      }

      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);

    return () => {
      cancelAnimationFrame(rafRef.current);
      host.removeEventListener("mousemove", onMove);
      host.removeEventListener("mouseleave", onLeave);
      host.removeEventListener("touchstart", onTouch);
      window.removeEventListener("resize", resize);
    };
  }, []);

  return (
    <React.Fragment>
      <canvas ref={bgRef} className="star-field star-field-bg" aria-hidden></canvas>
      <canvas ref={fgRef} className="star-field star-field-fg" aria-hidden></canvas>
    </React.Fragment>
  );
}

window.StarField = StarField;
