// animations.jsx — WebGL smoke background + global spotlight card effect

// ─── WebGL Smoke Background ──────────────────────────────────────────────────
const SMOKE_VERT = `attribute vec2 a_pos;void main(){gl_Position=vec4(a_pos,0.0,1.0);}`;

const SMOKE_FRAG = `
precision mediump float;
uniform float u_t;
uniform vec2 u_r;

float h(vec2 p){p=fract(p*vec2(234.34,435.345));p+=dot(p,p+34.23);return fract(p.x*p.y);}
float vn(vec2 p){
  vec2 i=floor(p),f=fract(p);
  f=f*f*(3.0-2.0*f);
  return mix(mix(h(i),h(i+vec2(1,0)),f.x),mix(h(i+vec2(0,1)),h(i+vec2(1,1)),f.x),f.y);
}
float fbm(vec2 p){
  float v=0.0,a=0.5;
  mat2 rot=mat2(1.6,1.2,-1.2,1.6);
  for(int i=0;i<5;i++){v+=a*vn(p);p=rot*p;a*=0.5;}
  return v;
}

void main(){
  vec2 uv=gl_FragCoord.xy/u_r;
  float t=u_t*0.055;

  vec2 p=uv*2.6;
  vec2 q=vec2(
    fbm(p+vec2(0.0,t)),
    fbm(p+vec2(5.2,1.3+t*0.75))
  );
  vec2 r=vec2(
    fbm(p+4.0*q+vec2(1.7,9.2)+vec2(t*0.12,0.0)),
    fbm(p+4.0*q+vec2(8.3,2.8)+vec2(0.0,t*0.09))
  );
  float f=fbm(p+4.0*r+vec2(t*0.035));

  // Shape mask — concentrate in upper portion of viewport
  // uv.y=0 is BOTTOM, uv.y=1 is TOP in WebGL
  float fy=1.0-uv.y;                             // 0=top, 1=bottom
  float mask=pow(clamp(f,0.0,1.0),2.2);
  mask*=smoothstep(0.0,0.07,fy);                 // tiny fade at very top edge
  mask*=(1.0-smoothstep(0.32,0.78,fy));          // fade out from 32% to 78% below top
  mask*=smoothstep(0.0,0.1,uv.x)*(1.0-smoothstep(0.9,1.0,uv.x)); // L/R edge fade

  // Colour — accent blue to ai purple tint
  vec3 blue  =vec3(0.30,0.54,1.00);
  vec3 purple=vec3(0.54,0.35,0.97);
  vec3 col=mix(blue,purple,clamp(q.x*0.9+f*0.45,0.0,1.0));

  gl_FragColor=vec4(col,mask*0.15);
}
`;

const SmokeBackground = () => {
  const ref = React.useRef(null);

  React.useEffect(() => {
    const canvas = ref.current;
    if (!canvas) return;

    const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
    if (!gl) { canvas.style.display = 'none'; return; }

    const compile = (type, src) => {
      const sh = gl.createShader(type);
      gl.shaderSource(sh, src);
      gl.compileShader(sh);
      if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
        console.warn('[smoke] shader error:', gl.getShaderInfoLog(sh));
        gl.deleteShader(sh);
        return null;
      }
      return sh;
    };

    const vs = compile(gl.VERTEX_SHADER, SMOKE_VERT);
    const fs = compile(gl.FRAGMENT_SHADER, SMOKE_FRAG);
    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('[smoke] link error:', gl.getProgramInfoLog(prog));
      return;
    }
    gl.useProgram(prog);

    // Full-screen triangle strip 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, 'a_pos');
    gl.enableVertexAttribArray(aPos);
    gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);

    const uT = gl.getUniformLocation(prog, 'u_t');
    const uR = gl.getUniformLocation(prog, 'u_r');

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.clearColor(0, 0, 0, 0);

    let raf, t0 = performance.now();

    const resize = () => {
      // Render at half resolution for perf, CSS scales it up
      const dpr = Math.min(window.devicePixelRatio || 1, 1.5);
      canvas.width  = Math.round(window.innerWidth  * dpr * 0.5);
      canvas.height = Math.round(window.innerHeight * dpr * 0.5);
      gl.viewport(0, 0, canvas.width, canvas.height);
    };
    resize();
    window.addEventListener('resize', resize);

    const tick = (now) => {
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.uniform1f(uT, (now - t0) / 1000);
      gl.uniform2f(uR, canvas.width, canvas.height);
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('resize', resize);
      gl.deleteBuffer(buf);
      gl.deleteShader(vs);
      gl.deleteShader(fs);
      gl.deleteProgram(prog);
    };
  }, []);

  return <canvas ref={ref} className="smoke-canvas" aria-hidden="true" />;
};
window.SmokeBackground = SmokeBackground;

// ─── Global Spotlight Effect ──────────────────────────────────────────────────
// Auto-attaches to pain-card, diff-card, pkg-card, ba-col via JS.
// Uses ::after pseudo-element (spotlight-card class) + CSS vars --sx / --sy.
const GlobalEffects = () => {
  React.useEffect(() => {
    const SEL = '.pain-card, .diff-card, .pkg-card, .ba-col';

    const attach = () => {
      document.querySelectorAll(SEL).forEach(card => {
        if (card.dataset.gfxSpot) return;
        card.dataset.gfxSpot = '1';
        card.classList.add('spotlight-card');

        card.addEventListener('mousemove', (e) => {
          const r = card.getBoundingClientRect();
          card.style.setProperty('--sx', `${e.clientX - r.left}px`);
          card.style.setProperty('--sy', `${e.clientY - r.top}px`);
        });
        card.addEventListener('mouseleave', () => {
          card.style.setProperty('--sx', '-9999px');
          card.style.setProperty('--sy', '-9999px');
        });
      });
    };

    // Observe DOM mutations (React updates) to catch newly rendered cards
    const obs = new MutationObserver(attach);
    obs.observe(document.body, { childList: true, subtree: true });
    setTimeout(attach, 400);

    return () => obs.disconnect();
  }, []);

  return null;
};
window.GlobalEffects = GlobalEffects;
