// Hero shader — WebGL fragment shader. Red plasma flow with warp + grid hints.
// Falls back to canvas2D variants ("orbital", "wireframe") if WebGL not available.

function Hero3D({ variant = "plasma", intensity = 65, accent = "#ff3324" }) {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(0);

  React.useEffect(() => {
    const cvs = canvasRef.current;
    if (!cvs) return;

    // Try WebGL for "plasma" variant
    if (variant === "plasma") {
      const gl = cvs.getContext("webgl", { antialias: false, premultipliedAlpha: false });
      if (gl) {
        const cleanup = initPlasma(cvs, gl, intensity, accent);
        return cleanup;
      }
      // fall through to canvas2D fallback if no WebGL
    }

    // Canvas 2D variants
    const cleanup = initCanvas2D(cvs, variant, intensity, accent);
    return cleanup;
  }, [variant, intensity, accent]);

  return (
    <canvas ref={canvasRef} aria-hidden="true" style={{
      position: "absolute", inset: 0,
      width: "100%", height: "100%",
      display: "block",
      pointerEvents: "none",
    }}/>
  );
}

// ── WebGL plasma shader ─────────────────────────────────────────
function initPlasma(cvs, gl, intensity, accent) {
  const VS = `
    attribute vec2 aPos;
    void main() { gl_Position = vec4(aPos, 0.0, 1.0); }
  `;

  const FS = `
    precision highp float;
    uniform vec2  iResolution;
    uniform float iTime;
    uniform vec3  uAccent;
    uniform float uIntensity;

    // Pseudo-random / hash
    float hash(float t) {
      return (cos(t) + cos(t * 1.3 + 1.3) + cos(t * 1.4 + 1.4)) / 3.0;
    }

    float drawSmoothLine(float pos, float halfWidth, float t) {
      return smoothstep(halfWidth, 0.0, abs(pos - t));
    }

    float drawCrispLine(float pos, float halfWidth, float t) {
      return smoothstep(halfWidth + 0.015, halfWidth, abs(pos - t));
    }

    void main() {
      vec2 fragCoord = gl_FragCoord.xy;
      vec2 uv = fragCoord / iResolution.xy;
      vec2 space = (fragCoord - iResolution.xy / 2.0) / iResolution.x * 2.0 * 5.0;

      float horizontalFade = 1.0 - (cos(uv.x * 6.28) * 0.5 + 0.5);
      float verticalFade   = 1.0 - (cos(uv.y * 6.28) * 0.5 + 0.5);

      // Warp space
      float warpAmp = 1.0;
      space.y += hash(space.x * 0.5 + iTime * 0.04) * warpAmp * (0.5 + horizontalFade);
      space.x += hash(space.y * 0.5 + iTime * 0.04 + 2.0) * warpAmp * horizontalFade;

      // Subtle grid (almost invisible)
      float gridLine = 0.0;
      gridLine += drawCrispLine(2.5, 0.012, abs(mod(space.x, 5.0) - 2.5));
      gridLine += drawCrispLine(2.5, 0.012, abs(mod(space.y, 5.0) - 2.5));
      gridLine = min(1.0, gridLine);

      // Flowing plasma lines
      vec3 plasma = vec3(0.0);
      const int LINE_COUNT = 14;
      for (int l = 0; l < LINE_COUNT; l++) {
        float fi = float(l);
        float offsetTime = iTime * 0.27;
        float offsetPos = fi + space.x * 0.5;
        float rand = hash(offsetPos + offsetTime) * 0.5 + 0.5;
        float halfW = mix(0.01, 0.2, rand * horizontalFade) / 2.0;
        float spread = mix(0.6, 2.2, horizontalFade);
        float offset = hash(offsetPos + offsetTime * (1.0 + fi / float(LINE_COUNT))) * spread;
        float linePos = hash(space.x * 0.2 + iTime * 0.2) * horizontalFade * 1.0 + offset;
        float line = drawSmoothLine(linePos, halfW, space.y) / 2.0
                   + drawCrispLine(linePos, halfW * 0.15, space.y);

        // Travelling glow dots
        float dotX = mod(fi + iTime * 0.2, 25.0) - 12.0;
        float dotY = hash(dotX * 0.2 + iTime * 0.2) * horizontalFade * 1.0 + offset;
        float dotDist = length(space - vec2(dotX, dotY));
        float dot = smoothstep(0.04, 0.0, dotDist) * 4.0;

        plasma += uAccent * (line + dot) * rand * 0.6;
      }

      // Background: dark canvas with subtle radial accent
      float d = length(uv - 0.5);
      vec3 bg = mix(vec3(0.04, 0.04, 0.04), uAccent * 0.08, 1.0 - smoothstep(0.0, 0.6, d));
      bg *= verticalFade;

      // Combine
      vec3 col = bg + plasma * uIntensity;
      col += uAccent * gridLine * 0.04;

      // Subtle vignette
      col *= 1.0 - smoothstep(0.55, 1.1, d) * 0.7;

      gl_FragColor = vec4(col, 1.0);
    }
  `;

  // Compile + link
  const compile = (type, src) => {
    const s = gl.createShader(type);
    gl.shaderSource(s, src);
    gl.compileShader(s);
    if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
      console.warn("Shader compile error:", gl.getShaderInfoLog(s));
      gl.deleteShader(s);
      return null;
    }
    return s;
  };
  const vs = compile(gl.VERTEX_SHADER, VS);
  const fs = compile(gl.FRAGMENT_SHADER, FS);
  if (!vs || !fs) return () => {};
  const prog = gl.createProgram();
  gl.attachShader(prog, vs);
  gl.attachShader(prog, fs);
  gl.linkProgram(prog);
  if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
    console.warn("Shader link error:", gl.getProgramInfoLog(prog));
    return () => {};
  }
  gl.useProgram(prog);

  // Full-screen quad
  const buf = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
  const aPos = gl.getAttribLocation(prog, "aPos");
  gl.enableVertexAttribArray(aPos);
  gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);

  const uRes = gl.getUniformLocation(prog, "iResolution");
  const uTime = gl.getUniformLocation(prog, "iTime");
  const uAcc = gl.getUniformLocation(prog, "uAccent");
  const uInt = gl.getUniformLocation(prog, "uIntensity");

  const accentRgb = hexToFloatRgb(accent);
  gl.uniform3f(uAcc, accentRgb[0], accentRgb[1], accentRgb[2]);
  gl.uniform1f(uInt, 0.45 + intensity / 100);

  let paused = false;
  let offscreen = false;
  let isMobile = false;
  const onVis = () => { paused = document.hidden; };
  document.addEventListener("visibilitychange", onVis);

  const visIo = new IntersectionObserver((entries) => {
    entries.forEach(e => { offscreen = !e.isIntersecting; });
  }, { threshold: 0 });
  visIo.observe(cvs);

  const resize = () => {
    const r = cvs.getBoundingClientRect();
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    isMobile = r.width < 720;
    const w = Math.max(1, Math.floor(r.width * dpr));
    const h = Math.max(1, Math.floor(r.height * dpr));
    cvs.width = w; cvs.height = h;
    gl.viewport(0, 0, w, h);
    gl.uniform2f(uRes, w, h);
  };
  resize();
  const ro = new ResizeObserver(resize);
  ro.observe(cvs);

  const t0 = performance.now();
  const targetFPS = 60;
  const frameInterval = 1000 / targetFPS;
  let lastFrame = 0;
  let rafId = 0;

  const render = (now) => {
    rafId = requestAnimationFrame(render);
    if (paused || offscreen) return;
    // throttle mobile to 30fps for battery
    const fi = isMobile ? 1000 / 30 : frameInterval;
    if (now - lastFrame < fi) return;
    lastFrame = now;
    const t = (now - t0) / 1000;
    gl.uniform1f(uTime, t);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  };
  rafId = requestAnimationFrame(render);

  return () => {
    cancelAnimationFrame(rafId);
    ro.disconnect();
    visIo.disconnect();
    document.removeEventListener("visibilitychange", onVis);
    gl.deleteBuffer(buf);
    gl.deleteProgram(prog);
    gl.deleteShader(vs);
    gl.deleteShader(fs);
  };
}

function hexToFloatRgb(hex) {
  const h = hex.replace("#", "");
  const n = parseInt(h.length === 3 ? h.split("").map(c => c + c).join("") : h, 16);
  return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];
}

// ── Canvas2D fallback variants (orbital / wireframe) ────────────
function initCanvas2D(cvs, variant, intensity, accent) {
  const ctx = cvs.getContext("2d", { alpha: true });
  const state = {
    mx: 0, my: 0, tx: 0, ty: 0,
    w: 0, h: 0, dpr: 1,
    paused: false, isMobile: false,
  };

  const resize = () => {
    const r = cvs.getBoundingClientRect();
    state.dpr = Math.min(window.devicePixelRatio || 1, 2);
    state.isMobile = r.width < 720;
    state.w = r.width; state.h = r.height;
    cvs.width = Math.max(1, Math.floor(r.width * state.dpr));
    cvs.height = Math.max(1, Math.floor(r.height * state.dpr));
    ctx.setTransform(state.dpr, 0, 0, state.dpr, 0, 0);
  };
  resize();
  const ro = new ResizeObserver(resize);
  ro.observe(cvs);

  const onMove = (e) => {
    const r = cvs.getBoundingClientRect();
    state.tx = ((e.clientX - r.left) / r.width  - 0.5);
    state.ty = ((e.clientY - r.top)  / r.height - 0.5);
  };
  window.addEventListener("pointermove", onMove, { passive: true });

  const onVis = () => { state.paused = document.hidden; };
  document.addEventListener("visibilitychange", onVis);

  const PCOUNT = Math.floor((state.isMobile ? 280 : 620) * (0.55 + intensity / 130));
  const sphere = Array.from({ length: PCOUNT }).map((_, i) => {
    const phi = Math.acos(1 - 2 * (i + 0.5) / PCOUNT);
    const theta = Math.PI * (1 + Math.sqrt(5)) * (i + 0.5);
    return { x: Math.cos(theta) * Math.sin(phi), y: Math.sin(theta) * Math.sin(phi), z: Math.cos(phi),
             seed: Math.random() * 1000, hot: Math.random() < 0.16 };
  });
  const rings = [0.22, 1.08, -0.62].map((tilt, idx) => ({
    tilt, idx,
    pts: Array.from({ length: state.isMobile ? 56 : 96 }).map((_, i, a) => i / a.length),
  }));

  const accentRgb = hexToRgbStr(accent);
  const intensityScale = 0.45 + intensity / 110;
  const t0 = performance.now();
  const targetInterval = 1000 / (state.isMobile ? 30 : 60);
  let lastFrame = 0;
  let rafId = 0;

  const frame = (now) => {
    rafId = requestAnimationFrame(frame);
    if (state.paused) return;
    if (now - lastFrame < targetInterval) return;
    lastFrame = now;
    const t = (now - t0) / 1000;
    const W = state.w, H = state.h;

    state.mx += (state.tx - state.mx) * 0.05;
    state.my += (state.ty - state.my) * 0.05;

    ctx.clearRect(0, 0, W, H);
    const cx = W / 2, cy = H / 2;
    const R = Math.min(W, H) * (state.isMobile ? 0.32 : 0.28);

    const bg = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(W, H) * 0.6);
    bg.addColorStop(0,    `rgba(${accentRgb}, ${0.20 * intensityScale})`);
    bg.addColorStop(0.35, `rgba(${accentRgb}, ${0.05 * intensityScale})`);
    bg.addColorStop(1, "rgba(0,0,0,0)");
    ctx.fillStyle = bg;
    ctx.fillRect(0, 0, W, H);

    const rotX = -0.32 + state.my * 0.35;
    const rotY = t * 0.10 * intensityScale + state.mx * 0.45;

    if (variant === "orbital") drawOrbital(ctx, sphere, rings, W, H, R, t, rotX, rotY, intensityScale, accentRgb);
    else drawWireframe(ctx, sphere, W, H, R, t, rotX, rotY, intensityScale, accentRgb);
  };
  rafId = requestAnimationFrame(frame);

  return () => {
    cancelAnimationFrame(rafId);
    ro.disconnect();
    window.removeEventListener("pointermove", onMove);
    document.removeEventListener("visibilitychange", onVis);
  };
}

function hexToRgbStr(hex) {
  const h = hex.replace("#", "");
  const n = parseInt(h.length === 3 ? h.split("").map(c => c + c).join("") : h, 16);
  return `${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}`;
}

function project(x, y, z, cx, cy, R, rotX, rotY) {
  const cyR = Math.cos(rotY), syR = Math.sin(rotY);
  const x1 = x * cyR - z * syR;
  const z1 = x * syR + z * cyR;
  const cxR = Math.cos(rotX), sxR = Math.sin(rotX);
  const y1 = y * cxR - z1 * sxR;
  const z2 = y * sxR + z1 * cxR;
  const FOV = 2.2, dist = 3.2;
  const scale = R * (FOV / (dist + z2));
  return { x: cx + x1 * scale, y: cy + y1 * scale, depth: (z2 + 1) / 2 };
}

function drawOrbital(ctx, sphere, rings, W, H, R, t, rotX, rotY, intensity, accentRgb) {
  const cx = W / 2, cy = H / 2;
  ctx.save();
  ctx.translate(cx, cy);
  for (let i = 1; i <= 5; i++) {
    ctx.strokeStyle = `rgba(255,255,255, ${0.022 + i * 0.008})`;
    ctx.lineWidth = 0.6;
    ctx.beginPath();
    ctx.arc(0, 0, R * (0.65 + i * 0.2), 0, Math.PI * 2);
    ctx.stroke();
  }
  ctx.restore();

  for (const ring of rings) {
    const ringR = 1.45;
    const tc = Math.cos(ring.tilt), ts = Math.sin(ring.tilt);
    const projected = ring.pts.map((u) => {
      const a = u * Math.PI * 2 + t * 0.32 * (1 + ring.idx * 0.45);
      const x0 = Math.cos(a) * ringR;
      const z0 = Math.sin(a) * ringR;
      const y = -z0 * ts;
      const z = z0 * tc;
      return project(x0, y, z, cx, cy, R, rotX, rotY);
    });
    ctx.beginPath();
    let drew = false;
    for (const p of projected) {
      if (p.depth < 0.35) { drew = false; continue; }
      if (!drew) { ctx.moveTo(p.x, p.y); drew = true; } else ctx.lineTo(p.x, p.y);
    }
    ctx.strokeStyle = `rgba(${accentRgb}, ${0.16 * intensity})`;
    ctx.lineWidth = 0.9;
    ctx.stroke();
    for (let i = 0; i < projected.length; i++) {
      const p = projected[i];
      if (p.depth < 0.35) continue;
      const r = 0.6 + p.depth * 2.0;
      const a = (p.depth - 0.3) * 1.3;
      const isAcc = i % 4 === 0;
      ctx.fillStyle = isAcc ? `rgba(${accentRgb}, ${a})` : `rgba(255,255,255, ${a * 0.6})`;
      ctx.beginPath();
      ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
      ctx.fill();
    }
  }

  const projected = sphere.map((v) => {
    const breathe = 1 + 0.045 * Math.sin(t * 0.85 + v.seed);
    return { ...project(v.x * breathe, v.y * breathe, v.z * breathe, cx, cy, R, rotX, rotY), hot: v.hot };
  }).sort((a, b) => a.depth - b.depth);
  for (const p of projected) {
    if (p.depth < 0.05) continue;
    const r = 0.4 + p.depth * 1.7;
    const a = (p.depth - 0.06) * 0.95;
    ctx.fillStyle = p.hot ? `rgba(${accentRgb}, ${a})` : `rgba(255, 255, 255, ${a * 0.7})`;
    ctx.beginPath();
    ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
    ctx.fill();
  }
  const core = ctx.createRadialGradient(cx, cy, 0, cx, cy, R * 0.45);
  core.addColorStop(0, `rgba(${accentRgb}, ${0.5 * intensity})`);
  core.addColorStop(0.5, `rgba(${accentRgb}, ${0.16 * intensity})`);
  core.addColorStop(1, "rgba(0,0,0,0)");
  ctx.fillStyle = core;
  ctx.fillRect(cx - R * 0.7, cy - R * 0.7, R * 1.4, R * 1.4);
}

function drawWireframe(ctx, sphere, W, H, R, t, rotX, rotY, intensity, accentRgb) {
  const cx = W / 2, cy = H / 2;
  const projected = sphere.map((v) => {
    const wob = 1 + 0.05 * (Math.sin(t * 0.7 + v.seed) + Math.cos(t * 1.1 + v.seed * 2)) * 0.5;
    return project(v.x * wob, v.y * wob, v.z * wob, cx, cy, R, rotX, rotY);
  });
  ctx.lineWidth = 0.65;
  for (let i = 0; i < projected.length; i++) {
    const a = projected[i];
    for (let off = 1; off <= 4; off++) {
      const b = projected[i + off];
      if (!b) continue;
      const d = (a.depth + b.depth) / 2;
      if (d < 0.42) continue;
      const dx = a.x - b.x, dy = a.y - b.y;
      if (Math.sqrt(dx * dx + dy * dy) > R * 0.42) continue;
      const al = (d - 0.42) * 1.2;
      ctx.strokeStyle = d > 0.78 ? `rgba(${accentRgb}, ${al * 0.85})` : `rgba(255,255,255, ${al * 0.4})`;
      ctx.beginPath();
      ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();
    }
  }
  for (const p of projected) {
    if (p.depth < 0.42) continue;
    ctx.fillStyle = p.depth > 0.85 ? `rgba(${accentRgb}, ${p.depth})` : `rgba(255,255,255, ${p.depth * 0.7})`;
    ctx.beginPath();
    ctx.arc(p.x, p.y, 0.5 + p.depth * 1.6, 0, Math.PI * 2);
    ctx.fill();
  }
  const core = ctx.createRadialGradient(cx, cy, 0, cx, cy, R * 0.55);
  core.addColorStop(0, `rgba(${accentRgb}, ${0.4 * intensity})`);
  core.addColorStop(1, "rgba(0,0,0,0)");
  ctx.fillStyle = core;
  ctx.fillRect(cx - R, cy - R, R * 2, R * 2);
}

window.Hero3D = Hero3D;
