const { useState: useStateS, useEffect: useEffectS, useRef: useRefS } = React;

/* ====== Base hook: shared canvas + mouse state ====== */
function useShaderCanvas(draw, deps) {
  const ref = useRefS(null);
  const stateRef = useRefS({
    mx: 0, my: 0, tx: 0, ty: 0,
    down: false, t: 0, clicks: [], ripples: [],
    w: 0, h: 0, dpr: Math.min(window.devicePixelRatio || 1, 2),
  });

  useEffectS(() => {
    const canvas = ref.current;
    if (!canvas) return;
    const host = canvas.parentElement;
    const ctx = canvas.getContext("2d");
    const S = stateRef.current;

    const resize = () => {
      const rect = host.getBoundingClientRect();
      S.w = rect.width;
      S.h = rect.height;
      canvas.width = S.w * S.dpr;
      canvas.height = S.h * S.dpr;
      canvas.style.width = S.w + "px";
      canvas.style.height = S.h + "px";
      ctx.setTransform(S.dpr, 0, 0, S.dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(host);

    S.mx = S.w / 2; S.my = S.h / 2;
    S.tx = S.mx; S.ty = S.my;

    const section = host.closest("section") || host;
    const toLocal = (e) => {
      const r = host.getBoundingClientRect();
      return { x: e.clientX - r.left, y: e.clientY - r.top };
    };
    const onMove = (e) => { const p = toLocal(e); S.mx = p.x; S.my = p.y; };
    const onDown = (e) => {
      S.down = true;
      const p = toLocal(e);
      S.clicks.push({ x: p.x, y: p.y, t: 0 });
      S.ripples.push({ x: p.x, y: p.y, r: 0, life: 1 });
    };
    const onUp = () => { S.down = false; };
    section.addEventListener("mousemove", onMove, { passive: true });
    section.addEventListener("mousedown", onDown);
    window.addEventListener("mouseup", onUp);
    section.style.pointerEvents = "auto";

    let raf;
    let last = performance.now();
    const loop = (now) => {
      const dt = Math.min(0.05, (now - last) / 1000);
      last = now;
      S.t += dt;
      S.tx += (S.mx - S.tx) * Math.min(1, dt * 6);
      S.ty += (S.my - S.ty) * Math.min(1, dt * 6);
      S.clicks = S.clicks.filter(c => (c.t += dt) < 1.2);
      S.ripples.forEach(r => { r.r += dt * 500; r.life -= dt * 0.8; });
      S.ripples = S.ripples.filter(r => r.life > 0);
      draw(ctx, S, dt);
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);

    return () => {
      cancelAnimationFrame(raf);
      ro.disconnect();
      section.removeEventListener("mousemove", onMove);
      section.removeEventListener("mousedown", onDown);
      window.removeEventListener("mouseup", onUp);
    };
  }, deps || []);

  return ref;
}

/* ====== 1. GRAVITY PARTICLES ====== */
function GravityWallpaper() {
  const particlesRef = useRefS(null);
  if (!particlesRef.current) {
    const N = 240;
    const arr = [];
    for (let i = 0; i < N; i++) {
      arr.push({
        x: Math.random() * window.innerWidth,
        y: Math.random() * window.innerHeight,
        vx: 0, vy: 0,
        r: 0.6 + Math.random() * 1.8,
        hue: Math.random() < 0.5 ? "#FF66CC" : "#A855F7",
      });
    }
    particlesRef.current = arr;
  }

  const ref = useShaderCanvas((ctx, S, dt) => {
    ctx.fillStyle = "rgba(9,9,11,0.18)";
    ctx.fillRect(0, 0, S.w, S.h);

    const parts = particlesRef.current;
    const gx = S.tx, gy = S.ty;
    const attract = S.down ? 1200 : 600;

    for (const p of parts) {
      const dx = gx - p.x, dy = gy - p.y;
      const d2 = dx*dx + dy*dy + 600;
      const f = attract / d2;
      p.vx += dx * f * dt;
      p.vy += dy * f * dt;
      p.vx *= 0.96; p.vy *= 0.96;
      p.x += p.vx; p.y += p.vy;

      // click impulse
      for (const c of S.clicks) {
        if (c.t < 0.3) {
          const ddx = p.x - c.x, ddy = p.y - c.y;
          const dd = Math.sqrt(ddx*ddx + ddy*ddy) + 0.1;
          const pulse = 800 * (1 - c.t/0.3) / (dd + 50);
          p.vx += (ddx/dd) * pulse * dt;
          p.vy += (ddy/dd) * pulse * dt;
        }
      }

      ctx.beginPath();
      ctx.arc(p.x, p.y, p.r, 0, Math.PI*2);
      ctx.fillStyle = p.hue;
      ctx.shadowBlur = 12;
      ctx.shadowColor = p.hue;
      ctx.fill();
    }
    ctx.shadowBlur = 0;
  });

  return <canvas ref={ref} className="shader-canvas" />;
}

/* ====== 2. METABALLS ====== */
function MetaballWallpaper() {
  const ballsRef = useRefS(null);
  if (!ballsRef.current) {
    const arr = [];
    for (let i = 0; i < 7; i++) {
      arr.push({
        x: Math.random() * window.innerWidth,
        y: Math.random() * window.innerHeight,
        vx: (Math.random()-0.5)*40,
        vy: (Math.random()-0.5)*40,
        r: 80 + Math.random()*80,
      });
    }
    ballsRef.current = arr;
  }

  const ref = useShaderCanvas((ctx, S, dt) => {
    ctx.fillStyle = "#09090B";
    ctx.fillRect(0, 0, S.w, S.h);

    const balls = ballsRef.current;
    // mouse ball
    const mouseBall = { x: S.tx, y: S.ty, r: S.down ? 220 : 150 };

    for (const b of balls) {
      b.x += b.vx * dt; b.y += b.vy * dt;
      if (b.x < 0 || b.x > S.w) b.vx *= -1;
      if (b.y < 0 || b.y > S.h) b.vy *= -1;
      // slight attraction to mouse
      const dx = S.tx - b.x, dy = S.ty - b.y;
      const d = Math.sqrt(dx*dx+dy*dy)+1;
      b.vx += (dx/d) * (S.down ? 80 : 20) * dt;
      b.vy += (dy/d) * (S.down ? 80 : 20) * dt;
      b.vx *= 0.995; b.vy *= 0.995;
    }

    const all = [...balls, mouseBall];
    for (const b of all) {
      const grd = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.r);
      const c1 = b === mouseBall ? "rgba(255,102,204,0.55)" : "rgba(168,85,247,0.35)";
      grd.addColorStop(0, c1);
      grd.addColorStop(1, "rgba(0,0,0,0)");
      ctx.fillStyle = grd;
      ctx.globalCompositeOperation = "screen";
      ctx.beginPath();
      ctx.arc(b.x, b.y, b.r, 0, Math.PI*2);
      ctx.fill();
    }
    ctx.globalCompositeOperation = "source-over";
  });

  return <canvas ref={ref} className="shader-canvas" />;
}

/* ====== 3. PLASMA RIPPLES ====== */
function PlasmaWallpaper() {
  const ref = useShaderCanvas((ctx, S, dt) => {
    // noise-ish gradient sweep
    ctx.fillStyle = "#09090B";
    ctx.fillRect(0, 0, S.w, S.h);

    const cols = 28, rows = 18;
    const cw = S.w / cols, ch = S.h / rows;
    for (let y = 0; y < rows; y++) {
      for (let x = 0; x < cols; x++) {
        const px = x * cw + cw/2;
        const py = y * ch + ch/2;
        const dx = px - S.tx, dy = py - S.ty;
        const d = Math.sqrt(dx*dx+dy*dy);
        const wave = Math.sin(d * 0.02 - S.t * 2) * 0.5 + 0.5;
        let rippleBoost = 0;
        for (const r of S.ripples) {
          const rd = Math.sqrt((px-r.x)**2 + (py-r.y)**2);
          const diff = Math.abs(rd - r.r);
          if (diff < 40) rippleBoost += r.life * (1 - diff/40) * 0.6;
        }
        const a = wave * 0.35 + rippleBoost;
        const hueMix = (Math.sin(S.t*0.6 + d*0.005) + 1)/2;
        const r = Math.round(255 * (0.6 + 0.4*hueMix));
        const g = Math.round(102 * hueMix + 85 * (1-hueMix));
        const b = Math.round(204);
        ctx.fillStyle = `rgba(${r},${g},${b},${Math.min(0.95, a)})`;
        ctx.fillRect(x*cw, y*ch, cw+1, ch+1);
      }
    }

    // cursor glow
    const grd = ctx.createRadialGradient(S.tx, S.ty, 0, S.tx, S.ty, 200);
    grd.addColorStop(0, "rgba(255,102,204,0.5)");
    grd.addColorStop(1, "rgba(0,0,0,0)");
    ctx.fillStyle = grd;
    ctx.beginPath();
    ctx.arc(S.tx, S.ty, 200, 0, Math.PI*2);
    ctx.fill();
  });

  return <canvas ref={ref} className="shader-canvas" />;
}

/* ====== 4. NEON GRID WARP ====== */
function GridWarpWallpaper() {
  const ref = useShaderCanvas((ctx, S, dt) => {
    ctx.fillStyle = "#09090B";
    ctx.fillRect(0, 0, S.w, S.h);

    const cell = 60;
    const wellR = S.down ? 380 : 260;
    const strength = S.down ? 120 : 60;

    ctx.strokeStyle = "rgba(255,102,204,0.4)";
    ctx.lineWidth = 1;

    const warp = (x, y) => {
      const dx = x - S.tx, dy = y - S.ty;
      const d = Math.sqrt(dx*dx+dy*dy);
      if (d > wellR) return { x, y };
      const falloff = (1 - d/wellR);
      const pull = falloff * falloff * strength;
      return { x: x - (dx/(d+0.1)) * pull, y: y - (dy/(d+0.1)) * pull };
    };

    // vertical lines
    for (let x = 0; x <= S.w; x += cell) {
      ctx.beginPath();
      for (let y = 0; y <= S.h; y += 8) {
        const p = warp(x, y);
        if (y === 0) ctx.moveTo(p.x, p.y);
        else ctx.lineTo(p.x, p.y);
      }
      ctx.stroke();
    }
    // horizontal lines
    for (let y = 0; y <= S.h; y += cell) {
      ctx.beginPath();
      for (let x = 0; x <= S.w; x += 8) {
        const p = warp(x, y);
        if (x === 0) ctx.moveTo(p.x, p.y);
        else ctx.lineTo(p.x, p.y);
      }
      ctx.stroke();
    }

    // gravity core
    const coreR = S.down ? 50 : 25;
    const g = ctx.createRadialGradient(S.tx, S.ty, 0, S.tx, S.ty, coreR*3);
    g.addColorStop(0, "rgba(255,102,204,0.9)");
    g.addColorStop(1, "rgba(255,102,204,0)");
    ctx.fillStyle = g;
    ctx.beginPath();
    ctx.arc(S.tx, S.ty, coreR*3, 0, Math.PI*2);
    ctx.fill();

    // ripples
    for (const r of S.ripples) {
      ctx.strokeStyle = `rgba(255,102,204,${r.life*0.6})`;
      ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.arc(r.x, r.y, r.r, 0, Math.PI*2);
      ctx.stroke();
    }
  });

  return <canvas ref={ref} className="shader-canvas" />;
}

/* ====== 5. AURORA FOLLOW ====== */
function AuroraFollowWallpaper() {
  const blobsRef = useRefS(null);
  if (!blobsRef.current) {
    blobsRef.current = [
      { x: 0.3, y: 0.3, r: 0.5, hue: "#FF66CC", phase: 0 },
      { x: 0.7, y: 0.4, r: 0.45, hue: "#A855F7", phase: 1.5 },
      { x: 0.5, y: 0.7, r: 0.4, hue: "#FF66CC", phase: 3 },
      { x: 0.2, y: 0.8, r: 0.35, hue: "#8b5cf6", phase: 4.5 },
    ];
  }

  const ref = useShaderCanvas((ctx, S, dt) => {
    ctx.fillStyle = "#09090B";
    ctx.fillRect(0, 0, S.w, S.h);

    const blobs = blobsRef.current;
    const mxN = S.tx / S.w - 0.5;
    const myN = S.ty / S.h - 0.5;

    ctx.globalCompositeOperation = "screen";
    ctx.filter = "blur(40px)";
    blobs.forEach((b, i) => {
      const drift = Math.sin(S.t * 0.3 + b.phase) * 0.08;
      const pullX = mxN * (S.down ? 0.35 : 0.18) * (i % 2 === 0 ? 1 : -1);
      const pullY = myN * (S.down ? 0.35 : 0.18) * (i % 2 === 0 ? 1 : -1);
      const cx = (b.x + pullX + drift) * S.w;
      const cy = (b.y + pullY) * S.h;
      const cr = b.r * Math.min(S.w, S.h) * (S.down ? 1.1 : 1);
      const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, cr);
      g.addColorStop(0, b.hue);
      g.addColorStop(1, "rgba(0,0,0,0)");
      ctx.fillStyle = g;
      ctx.beginPath();
      ctx.arc(cx, cy, cr, 0, Math.PI*2);
      ctx.fill();
    });
    ctx.filter = "none";
    ctx.globalCompositeOperation = "source-over";

    // dim vignette
    const v = ctx.createRadialGradient(S.w/2, S.h*0.3, 0, S.w/2, S.h*0.5, Math.max(S.w,S.h)*0.8);
    v.addColorStop(0, "rgba(0,0,0,0)");
    v.addColorStop(1, "rgba(9,9,11,0.6)");
    ctx.fillStyle = v;
    ctx.fillRect(0, 0, S.w, S.h);
  });

  return <canvas ref={ref} className="shader-canvas" />;
}

/* ====== ROUTER ====== */
const WALLPAPERS = [
  { id: "aurora",  label: "aurora.follow",  cmp: AuroraFollowWallpaper, desc: "pink blobs drift toward cursor" },
  { id: "gravity", label: "gravity.field",   cmp: GravityWallpaper,      desc: "particles pulled by gravity · click = burst" },
  { id: "meta",    label: "metaballs.flow",  cmp: MetaballWallpaper,     desc: "liquid orbs merge around mouse" },
  { id: "plasma",  label: "plasma.ripple",   cmp: PlasmaWallpaper,       desc: "psy-wave · click drops ripple" },
  { id: "grid",    label: "neon.warp",       cmp: GridWarpWallpaper,     desc: "gravity well bends the grid" },
];

function Wallpaper({ id }) {
  const w = WALLPAPERS.find(x => x.id === id) || WALLPAPERS[0];
  const Cmp = w.cmp;
  return <Cmp key={w.id} />;
}

window.WALLPAPERS = WALLPAPERS;
window.Wallpaper = Wallpaper;
