/* ══════════════════════════════════════════════════════
   PLASMA WEB — animated WebGL background (adapted from ogl)
   Loads `ogl` dynamically from esm.sh, exposes window.PlasmaWeb
   ══════════════════════════════════════════════════════ */
const PLASMA_VERT = `
attribute vec2 uv;
attribute vec2 position;
varying vec2 vUv;
void main() {
  vUv = uv;
  gl_Position = vec4(position, 0, 1);
}
`;

const PLASMA_FRAG = `
precision highp float;

uniform float uTime;
uniform vec3 uResolution;
uniform vec2 uFocal;
uniform float uFlowSpeed;
uniform float uDensity;
uniform float uHueShift;
uniform float uSpeed;
uniform vec2 uMouse;
uniform float uGlowIntensity;
uniform float uSaturation;
uniform bool uMouseAttraction;
uniform float uPulseIntensity;
uniform float uWebComplexity;
uniform float uAttractionStrength;
uniform float uMouseActiveFactor;
uniform float uEnergyFlow;
uniform bool uTransparent;
uniform float uBrightness;

varying vec2 vUv;

#define NUM_LAYERS 3.0
#define PI 3.14159265359

float Hash21(vec2 p) {
  p = fract(p * vec2(234.67, 891.23));
  p += dot(p, p + 56.78);
  return fract(p.x * p.y);
}

float Hash11(float p) {
  p = fract(p * 345.23);
  p += p * p;
  return fract(p);
}

vec3 hsv2rgb(vec3 c) {
  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

float noise(vec2 p) {
  vec2 i = floor(p);
  vec2 f = fract(p);
  vec2 u = f * f * (3.0 - 2.0 * f);
  return mix(mix(Hash21(i + vec2(0.0,0.0)),
                 Hash21(i + vec2(1.0,0.0)), u.x),
             mix(Hash21(i + vec2(0.0,1.0)),
                 Hash21(i + vec2(1.0,1.0)), u.x), u.y);
}

float fbm(vec2 p) {
  float value = 0.0;
  float amplitude = 0.5;
  float frequency = 1.0;
  for(int i = 0; i < 4; i++) {
    value += amplitude * noise(p * frequency);
    amplitude *= 0.5;
    frequency *= 2.0;
  }
  return value;
}

vec2 curl(vec2 p) {
  float eps = 0.01;
  float n1 = fbm(p + vec2(eps, 0.0));
  float n2 = fbm(p - vec2(eps, 0.0));
  float n3 = fbm(p + vec2(0.0, eps));
  float n4 = fbm(p - vec2(0.0, eps));
  return vec2(n4 - n3, n1 - n2) / (2.0 * eps);
}

float PlasmaNode(vec2 uv, vec2 offset, float seed, float time) {
  vec2 nodePos = offset;
  nodePos += 0.3 * sin(time * 0.5 + seed * 6.28) * vec2(cos(seed * 12.34), sin(seed * 23.45));
  float dist = length(uv - nodePos);
  float pulse = sin(time * 2.0 + seed * 10.0) * 0.5 + 0.5;
  float intensity = (0.02 + 0.01 * pulse) / (dist + 0.01);
  return intensity * smoothstep(0.8, 0.0, dist);
}

vec3 PlasmaWeb(vec2 uv) {
  vec3 col = vec3(0.0);
  float time = uTime * uSpeed;

  vec2 flow = curl(uv * 2.0 + time * 0.1);
  uv += flow * uEnergyFlow * 0.1;

  vec2 gridSize = vec2(4.0 * uDensity, 3.0 * uDensity);
  vec2 grid = uv * gridSize;
  vec2 gridId = floor(grid);
  vec2 gridUv = fract(grid) - 0.5;

  for(int y = -1; y <= 1; y++) {
    for(int x = -1; x <= 1; x++) {
      vec2 offset = vec2(float(x), float(y));
      vec2 nodeId = gridId + offset;
      float seed = Hash21(nodeId);
      vec2 nodeOffset = vec2(Hash21(nodeId + 1.0), Hash21(nodeId + 2.0)) - 0.5;
      nodeOffset *= 0.8;
      float node = PlasmaNode(gridUv, offset + nodeOffset, seed, time);
      float hue = Hash21(nodeId + 3.0) + uHueShift / 360.0 + time * 0.05;
      hue = fract(hue);
      vec3 nodeColor = hsv2rgb(vec3(hue, uSaturation, uBrightness));
      col += node * nodeColor;
    }
  }

  float streamNoise = fbm(uv * 8.0 + time * 0.2);
  float streams = smoothstep(0.3, 0.7, streamNoise);
  streams *= smoothstep(0.0, 0.1, sin(uv.x * 15.0 + time)) * 0.3;
  streams += smoothstep(0.0, 0.1, sin(uv.y * 12.0 + time * 1.2)) * 0.3;
  float streamHue = fract(uHueShift / 360.0 + 0.5 + time * 0.03);
  vec3 streamColor = hsv2rgb(vec3(streamHue, uSaturation * 0.8, uBrightness * 0.6));
  col += streams * streamColor * uGlowIntensity;

  vec2 particleUv = uv * 20.0 + time * vec2(0.5, 0.3);
  float particles = 0.0;
  for(int i = 0; i < 3; i++) {
    vec2 pid = floor(particleUv + float(i) * 123.45);
    float pseed = Hash21(pid);
    if(pseed > 0.85) {
      vec2 ppos = fract(particleUv + float(i) * 123.45) - 0.5;
      ppos += 0.3 * sin(time + pseed * 20.0) * vec2(cos(pseed * 15.0), sin(pseed * 18.0));
      float pdist = length(ppos);
      float particle = (0.005 * uGlowIntensity) / (pdist + 0.001);
      particle *= smoothstep(0.3, 0.0, pdist);
      particles += particle;
    }
  }
  col += particles * hsv2rgb(vec3(fract(uHueShift / 360.0 + 0.8), uSaturation, uBrightness));

  return col;
}

void main() {
  vec2 focalPx = uFocal * uResolution.xy;
  vec2 uv = (vUv * uResolution.xy - focalPx) / uResolution.y;

  if(uMouseAttraction && uMouseActiveFactor > 0.0) {
    vec2 mousePosUV = (uMouse * uResolution.xy - focalPx) / uResolution.y;
    vec2 toMouse = mousePosUV - uv;
    float mouseDist = length(toMouse);
    vec2 attraction = normalize(toMouse) * (uAttractionStrength / (mouseDist * mouseDist + 1.0));
    uv += attraction * 0.1 * uMouseActiveFactor;
  }

  vec3 col = vec3(0.0);
  for(float i = 0.0; i < 1.0; i += 1.0 / NUM_LAYERS) {
    float depth = i + 0.5;
    float scale = mix(1.0, 2.0, depth);
    float fade = (1.0 - depth * depth) * 0.8;
    vec3 layerCol = PlasmaWeb(uv * scale + i * 100.0);
    col += layerCol * fade;
  }

  float pulse = sin(uTime * uSpeed * 2.0) * 0.5 + 0.5;
  col *= 1.0 + pulse * uPulseIntensity * 0.3;

  if(uTransparent) {
    float alpha = length(col);
    alpha = smoothstep(0.0, 0.5, alpha);
    alpha = min(alpha, 1.0);
    gl_FragColor = vec4(col, alpha);
  } else {
    gl_FragColor = vec4(col, 1.0);
  }
}
`;

function PlasmaWeb({
  focal = [0.5, 0.5],
  flowSpeed = 0.5,
  density = 1,
  hueShift = 240,
  disableAnimation = false,
  speed = 1.0,
  mouseInteraction = true,
  glowIntensity = 0.8,
  saturation = 0.7,
  mouseAttraction = true,
  pulseIntensity = 0.4,
  webComplexity = 1.0,
  attractionStrength = 1.0,
  energyFlow = 1.0,
  transparent = true,
  brightness = 0.9,
  style,
  className,
}) {
  const ctnDom = React.useRef(null);
  const targetMousePos = React.useRef({ x: 0.5, y: 0.5 });
  const smoothMousePos = React.useRef({ x: 0.5, y: 0.5 });
  const targetMouseActive = React.useRef(0.0);
  const smoothMouseActive = React.useRef(0.0);

  React.useEffect(() => {
    if (!ctnDom.current) return;
    const ctn = ctnDom.current;
    let cleanup = () => {};
    let cancelled = false;

    function initPlasma(OGL) {
      if (cancelled || !ctn) return;
      const { Renderer, Program, Mesh, Color, Triangle } = OGL;

      const renderer = new Renderer({ alpha: transparent, premultipliedAlpha: false });
      const gl = renderer.gl;

      if (transparent) {
        gl.enable(gl.BLEND);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
        gl.clearColor(0, 0, 0, 0);
      } else {
        gl.clearColor(0, 0, 0, 1);
      }

      let program;
      function resize() {
        renderer.setSize(ctn.offsetWidth, ctn.offsetHeight);
        if (program) {
          program.uniforms.uResolution.value = new Color(
            gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height
          );
        }
      }
      window.addEventListener('resize', resize, false);
      resize();

      const geometry = new Triangle(gl);
      program = new Program(gl, {
        vertex: PLASMA_VERT,
        fragment: PLASMA_FRAG,
        uniforms: {
          uTime: { value: 0 },
          uResolution: { value: new Color(gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height) },
          uFocal: { value: new Float32Array(focal) },
          uFlowSpeed: { value: flowSpeed },
          uDensity: { value: density },
          uHueShift: { value: hueShift },
          uSpeed: { value: speed },
          uMouse: { value: new Float32Array([smoothMousePos.current.x, smoothMousePos.current.y]) },
          uGlowIntensity: { value: glowIntensity },
          uSaturation: { value: saturation },
          uMouseAttraction: { value: mouseAttraction },
          uPulseIntensity: { value: pulseIntensity },
          uWebComplexity: { value: webComplexity },
          uAttractionStrength: { value: attractionStrength },
          uMouseActiveFactor: { value: 0.0 },
          uEnergyFlow: { value: energyFlow },
          uTransparent: { value: transparent },
          uBrightness: { value: brightness },
        },
      });

      const mesh = new Mesh(gl, { geometry, program });
      let animateId;

      function update(t) {
        animateId = requestAnimationFrame(update);
        if (!disableAnimation) program.uniforms.uTime.value = t * 0.001;
        const lerp = 0.08;
        smoothMousePos.current.x += (targetMousePos.current.x - smoothMousePos.current.x) * lerp;
        smoothMousePos.current.y += (targetMousePos.current.y - smoothMousePos.current.y) * lerp;
        smoothMouseActive.current += (targetMouseActive.current - smoothMouseActive.current) * lerp;
        program.uniforms.uMouse.value[0] = smoothMousePos.current.x;
        program.uniforms.uMouse.value[1] = smoothMousePos.current.y;
        program.uniforms.uMouseActiveFactor.value = smoothMouseActive.current;
        renderer.render({ scene: mesh });
      }
      animateId = requestAnimationFrame(update);
      ctn.appendChild(gl.canvas);
      gl.canvas.style.width = '100%';
      gl.canvas.style.height = '100%';
      gl.canvas.style.display = 'block';

      function handleMouseMove(e) {
        const rect = ctn.getBoundingClientRect();
        const x = (e.clientX - rect.left) / rect.width;
        const y = 1.0 - (e.clientY - rect.top) / rect.height;
        targetMousePos.current = { x, y };
        targetMouseActive.current = 1.0;
      }
      function handleMouseLeave() { targetMouseActive.current = 0.0; }

      if (mouseInteraction) {
        ctn.addEventListener('mousemove', handleMouseMove);
        ctn.addEventListener('mouseleave', handleMouseLeave);
      }

      cleanup = () => {
        cancelAnimationFrame(animateId);
        window.removeEventListener('resize', resize);
        if (mouseInteraction) {
          ctn.removeEventListener('mousemove', handleMouseMove);
          ctn.removeEventListener('mouseleave', handleMouseLeave);
        }
        try { ctn.removeChild(gl.canvas); } catch (e) {}
        gl.getExtension('WEBGL_lose_context')?.loseContext();
      };
    }

    const cached = window.__oglModule;
    if (cached && cached.Renderer) {
      initPlasma(cached);
    } else {
      const nativeImport = new Function('u', 'return import(u)');
      nativeImport('https://cdn.jsdelivr.net/npm/ogl@1.0.11/+esm')
        .then((mod) => { window.__oglModule = mod; initPlasma(mod); })
        .catch((err) => console.error('PlasmaWeb: failed to load ogl —', (err && (err.message || err.name)) || err));
    }

    return () => { cancelled = true; cleanup(); };
  }, [focal, flowSpeed, density, hueShift, disableAnimation, speed, mouseInteraction,
      glowIntensity, saturation, mouseAttraction, pulseIntensity, webComplexity,
      attractionStrength, energyFlow, transparent, brightness]);

  return <div ref={ctnDom} className={className} style={{ width: '100%', height: '100%', position: 'relative', ...style }} />;
}

window.PlasmaWeb = PlasmaWeb;
