// ShaderAnimation — Three.js fullscreen fragment-shader background.
// Ported from the shadcn/TS demo to this project's setup:
//   • Three.js via CDN global (window.THREE), no `import * as THREE`
//   • container-sized (ResizeObserver) instead of `w-full h-screen` + window
//   • fragment shader rewritten to MONOCHROME — white ripple lines on a
//     transparent canvas (alpha = intensity) so it composites over the
//     black page and matches the B&W editorial system. Original was RGB.
//
// Usage: <ShaderAnimation opacity={0.4} /> inside a positioned ancestor.

const { useEffect: useE_sa, useRef: useR_sa } = React;

function ShaderAnimation({ opacity = 0.4, colored = false, style = {} }) {
  const containerRef = useR_sa(null);

  useE_sa(() => {
    const THREE = window.THREE;
    const container = containerRef.current;
    if (!THREE || !container) return;

    const vertexShader = `
      void main() {
        gl_Position = vec4( position, 1.0 );
      }
    `;

    // Monochrome: accumulate a single intensity, output white with alpha = c.
    const fragmentMono = `
      precision highp float;
      uniform vec2 resolution;
      uniform float time;

      void main(void) {
        vec2 uv = (gl_FragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);
        float t = time * 0.05;
        float lineWidth = 0.0022;

        float c = 0.0;
        for(int i = 0; i < 5; i++){
          c += lineWidth * float(i*i) /
               abs(fract(t + float(i)*0.01) * 5.0 - length(uv) + mod(uv.x + uv.y, 0.2));
        }
        c = clamp(c, 0.0, 1.0);
        gl_FragColor = vec4(vec3(1.0), c);
      }
    `;

    // Colored: per-channel chromatic offset (the original effect). Alpha is the
    // brightest channel so dark areas stay transparent over the black page.
    const fragmentColor = `
      precision highp float;
      uniform vec2 resolution;
      uniform float time;

      void main(void) {
        vec2 uv = (gl_FragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);
        float t = time * 0.05;
        float lineWidth = 0.0022;

        vec3 color = vec3(0.0);
        for(int j = 0; j < 3; j++){
          for(int i = 0; i < 5; i++){
            color[j] += lineWidth * float(i*i) /
              abs(fract(t - 0.01*float(j) + float(i)*0.01) * 5.0 - length(uv) + mod(uv.x + uv.y, 0.2));
          }
        }
        color = clamp(color, 0.0, 1.0);
        float a = clamp(max(color.r, max(color.g, color.b)), 0.0, 1.0);
        gl_FragColor = vec4(color, a);
      }
    `;

    const camera = new THREE.Camera();
    camera.position.z = 1;

    const scene = new THREE.Scene();
    const geometry = new THREE.PlaneGeometry(2, 2);

    const uniforms = {
      time: { value: 1.0 },
      resolution: { value: new THREE.Vector2() },
    };

    const material = new THREE.ShaderMaterial({
      uniforms,
      vertexShader,
      fragmentShader: colored ? fragmentColor : fragmentMono,
      transparent: true,
    });

    const mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setClearColor(0x000000, 0);
    container.appendChild(renderer.domElement);
    renderer.domElement.style.display = "block";

    const onResize = () => {
      const width = container.clientWidth || window.innerWidth;
      const height = container.clientHeight || window.innerHeight;
      renderer.setSize(width, height);
      uniforms.resolution.value.x = renderer.domElement.width;
      uniforms.resolution.value.y = renderer.domElement.height;
    };
    onResize();

    const ro = new ResizeObserver(onResize);
    ro.observe(container);
    window.addEventListener("resize", onResize);

    let animationId;
    const animate = () => {
      animationId = requestAnimationFrame(animate);
      uniforms.time.value += 0.05;
      renderer.render(scene, camera);
    };
    animate();

    return () => {
      ro.disconnect();
      window.removeEventListener("resize", onResize);
      cancelAnimationFrame(animationId);
      geometry.dispose();
      material.dispose();
      renderer.dispose();
      if (renderer.domElement && renderer.domElement.parentNode === container) {
        container.removeChild(renderer.domElement);
      }
    };
  }, [opacity, colored]);

  return (
    <div
      ref={containerRef}
      aria-hidden="true"
      style={{
        position: "absolute",
        inset: 0,
        opacity,
        pointerEvents: "none",
        overflow: "hidden",
        ...style,
      }}
    />
  );
}

window.ShaderAnimation = ShaderAnimation;
