// Background — animated noise + scanlines + subtle RGB drift
// Pure CSS/SVG, no shaders required.

function GenerativeBG({ intensity = 0.4 }) {
  const canvasRef = React.useRef(null);
  const intensityRef = React.useRef(intensity);
  intensityRef.current = intensity;
  const mouseRef = React.useRef({ x: -9999, y: -9999, speed: 0 });

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const buf = document.createElement('canvas');
    const bufCtx = buf.getContext('2d');

    // Sample theme colors once — getComputedStyle always returns rgb() regardless of input format
    const tempEl = document.createElement('div');
    tempEl.style.cssText = 'position:absolute;color:var(--accent);visibility:hidden';
    document.body.appendChild(tempEl);
    const accentRgb = getComputedStyle(tempEl).color;
    document.body.removeChild(tempEl);
    const accentMatch = accentRgb.match(/(\d+),\s*(\d+),\s*(\d+)/);
    const [ar, ag, ab] = accentMatch ? accentMatch.slice(1).map(Number) : [200, 138, 62];

    const resize = () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      buf.width = canvas.width;
      buf.height = canvas.height;
    };
    resize();
    window.addEventListener('resize', resize);

    const onMouseMove = (e) => {
      const dx = e.clientX - mouseRef.current.x;
      const dy = e.clientY - mouseRef.current.y;
      mouseRef.current = {
        x: e.clientX,
        y: e.clientY,
        speed: Math.sqrt(dx * dx + dy * dy),
      };
    };
    window.addEventListener('mousemove', onMouseMove);

    // Repeller rects — cached from DOM, refreshed on scroll/resize
    let repellers = [];
    const updateRepellers = () => {
      repellers = Array.from(
        document.querySelectorAll('.pcard-media, .ws-card-media, .topnav')
      ).map(el => el.getBoundingClientRect()).filter(r => r.width > 0);
    };
    updateRepellers();
    window.addEventListener('scroll', updateRepellers, { passive: true });

    const particles = Array.from({ length: 280 }, () => ({
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
      life: Math.random() * 300,
      maxLife: 200 + Math.random() * 300,
      speed: 0.3 + Math.random() * 0.5,
      size: 0.4 + Math.random() * 1.2,
    }));

    const noise = (x, y, t) =>
      Math.sin(x * 1.2 + t * 0.5) * Math.cos(y * 0.9 - t * 0.4) +
      Math.sin(x * 0.4 - y * 0.6 + t * 0.3) * 0.6 +
      Math.cos(x * 0.7 + y * 0.5 + t * 0.7) * 0.3;

    let raf;
    let repellerTick = 0;
    const tick = () => {
      if (++repellerTick % 45 === 0) updateRepellers();
      const t = Date.now() * 0.00025;
      const w = canvas.width;
      const h = canvas.height;
      const iv = intensityRef.current;

      // Capture trails before fade
      bufCtx.clearRect(0, 0, w, h);
      bufCtx.drawImage(canvas, 0, 0);

      // Fade toward actual theme background so particles never accumulate
      const bg = getComputedStyle(document.documentElement).getPropertyValue('--bg').trim() || '#15120e';
      ctx.globalAlpha = 0.06;
      ctx.fillStyle = bg;
      ctx.fillRect(0, 0, w, h);
      ctx.globalAlpha = 1;

      // Displace existing trails around cursor
      const mx = mouseRef.current.x;
      const my = mouseRef.current.y;
      if (mx > 0) {
        const r = 88;
        const expand = Math.min(mouseRef.current.speed * 0.18, 10);
        if (expand > 0.3) {
          ctx.save();
          ctx.beginPath();
          ctx.arc(mx, my, r, 0, Math.PI * 2);
          ctx.clip();
          ctx.globalAlpha = 0.65;
          ctx.drawImage(buf,
            mx - r,          my - r,          r * 2,              r * 2,
            mx - r - expand, my - r - expand, r * 2 + expand * 2, r * 2 + expand * 2
          );
          ctx.globalAlpha = 1;
          ctx.restore();
        }
      }

      for (const p of particles) {
        const angle = noise(p.x * 0.003, p.y * 0.003, t) * Math.PI * 2.5;
        p.x += Math.cos(angle) * p.speed;
        p.y += Math.sin(angle) * p.speed;

        const dx = p.x - mouseRef.current.x;
        const dy = p.y - mouseRef.current.y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        if (dist < 100 && dist > 0) {
          const force = (1 - dist / 100) * 1.8;
          p.x += (dx / dist) * force;
          p.y += (dy / dist) * force;
        }

        // Repel from card/UI elements
        for (const r of repellers) {
          const cx = Math.max(r.left, Math.min(p.x, r.right));
          const cy = Math.max(r.top,  Math.min(p.y, r.bottom));
          const ex = p.x - cx, ey = p.y - cy;
          const ed = ex * ex + ey * ey;
          if (ed > 0 && ed < 2500) { // 50px radius
            const d = Math.sqrt(ed);
            const f = (1 - d / 50) * 2.2;
            p.x += (ex / d) * f;
            p.y += (ey / d) * f;
          }
        }

        p.life++;

        if (p.x < 0) p.x += w;
        else if (p.x > w) p.x -= w;
        if (p.y < 0) p.y += h;
        else if (p.y > h) p.y -= h;

        if (p.life > p.maxLife) {
          p.x = Math.random() * w;
          p.y = Math.random() * h;
          p.life = 0;
        }

        const alpha = Math.sin((p.life / p.maxLife) * Math.PI) * 0.14 * iv;
        ctx.fillStyle = `rgba(${ar}, ${ag}, ${ab}, ${alpha.toFixed(3)})`;
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
        ctx.fill();
      }

      raf = requestAnimationFrame(tick);
    };
    tick();

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('resize', resize);
      window.removeEventListener('mousemove', onMouseMove);
      window.removeEventListener('scroll', updateRepellers);
    };
  }, []);

  return (
    <div className="bg-fx" aria-hidden="true">
      <canvas ref={canvasRef} className="bg-canvas" />
      <div className="bg-vignette"></div>
    </div>
  );
}

// Custom cursor — a small ring with a trailing dot, optionally distorts media
function CustomCursor({ mode = "trail" }) {
  const ringRef = React.useRef(null);
  const dotRef = React.useRef(null);
  const labelRef = React.useRef(null);
  const stateRef = React.useRef({ x: 0, y: 0, tx: 0, ty: 0, scale: 1, ts: 1, label: "" });

  React.useEffect(() => {
    if (mode === "off") return;
    const onMove = (e) => {
      stateRef.current.tx = e.clientX;
      stateRef.current.ty = e.clientY;
      const el = e.target.closest("[data-cursor]");
      const newLabel = el ? el.getAttribute("data-cursor") : "";
      const newScale = el ? 2.6 : 1;
      stateRef.current.ts = newScale;
      if (newLabel !== stateRef.current.label) {
        stateRef.current.label = newLabel;
        if (labelRef.current) {
          labelRef.current.textContent = newLabel;
          labelRef.current.style.opacity = newLabel ? "1" : "0";
        }
      }
    };
    window.addEventListener("mousemove", onMove);

    let raf;
    const tick = () => {
      const s = stateRef.current;
      s.x += (s.tx - s.x) * 0.18;
      s.y += (s.ty - s.y) * 0.18;
      s.scale += (s.ts - s.scale) * 0.14;
      if (ringRef.current) {
        ringRef.current.style.transform = `translate(${s.x}px, ${s.y}px) translate(-50%, -50%) scale(${s.scale})`;
      }
      if (dotRef.current) {
        dotRef.current.style.transform = `translate(${s.tx}px, ${s.ty}px) translate(-50%, -50%)`;
      }
      if (labelRef.current) {
        labelRef.current.style.transform = `translate(${s.tx + 20}px, ${s.ty + 20}px)`;
      }
      raf = requestAnimationFrame(tick);
    };
    tick();
    return () => {
      window.removeEventListener("mousemove", onMove);
      cancelAnimationFrame(raf);
    };
  }, [mode]);

  if (mode === "off") return null;
  return (
    <>
      <div ref={ringRef} className={`cur-ring cur-${mode}`}></div>
      <div ref={dotRef} className="cur-dot"></div>
      <div ref={labelRef} className="cur-label"></div>
    </>
  );
}

// Reusable media placeholder — renders a colored panel with the project's
// label, file metadata and a play indicator. Mimics video-on-hover behavior
// with an animated gradient + subtle "scrub" line.
function MediaTile({ media, title, idx = 0, onClick, large = false }) {
  const ratioMap = { "16:9": "16 / 9", "9:16": "9 / 16", "1:1": "1 / 1", "21:9": "21 / 9", "3:2": "3 / 2", "4:3": "4 / 3" };
  const aspectRatio = ratioMap[media.aspect] || "16 / 9";
  return (
    <div
      className="tile"
      data-cursor={media.kind === "video" ? "PLAY ●" : "VIEW"}
      onClick={onClick}
      style={{ aspectRatio }}
    >
      <div className="tile-fx" style={{ "--c": media.color || "oklch(0.42 0.05 60)" }}></div>
      <div className="tile-meta">
        <span className="tile-kind">{media.kind === "video" ? "▶ VIDEO" : "◇ STILL"}</span>
        <span className="tile-aspect">{media.aspect}</span>
      </div>
      {large && <div className="tile-label">{media.label || title}</div>}
      <div className="tile-glitch"></div>
    </div>
  );
}

Object.assign(window, { GenerativeBG, CustomCursor, MediaTile });
