"use client";

import { useEffect, useRef } from "react";
import { Mesh, Plane, Program, Renderer, Texture } from "ogl";

const vertex = `
  attribute vec2 uv;
  attribute vec2 position;
  varying vec2 vUv;

  void main() {
    vUv = uv;
    gl_Position = vec4(position, 0.0, 1.0);
  }
`;

const fragment = `
  precision highp float;

  uniform sampler2D tMap;
  uniform vec2 uResolution;
  uniform vec2 uImageResolution;
  uniform vec2 uPointer;
  uniform vec2 uVelocity;
  uniform float uStrength;
  varying vec2 vUv;

  vec2 coverUv(vec2 uv, vec2 screen, vec2 image) {
    float screenRatio = screen.x / screen.y;
    float imageRatio = image.x / image.y;
    vec2 scale = vec2(1.0);

    if (screenRatio > imageRatio) {
      scale.y = imageRatio / screenRatio;
    } else {
      scale.x = screenRatio / imageRatio;
    }

    return (uv - 0.5) * scale + 0.5;
  }

  void main() {
    vec2 uv = coverUv(vUv, uResolution, uImageResolution);

    vec2 pointerDelta = uv - uPointer;
    float distanceFromPointer = length(pointerDelta);
    float cursorMask = smoothstep(0.36, 0.0, distanceFromPointer);
    float innerMask = smoothstep(0.16, 0.0, distanceFromPointer);
    vec2 pull = normalize(pointerDelta + 0.0001) * innerMask * 0.0035;
    vec2 distortion = ((uVelocity * 0.075) - pull) * cursorMask * uStrength;

    uv += distortion;

    float red = texture2D(tMap, uv + distortion * 0.12).r;
    float green = texture2D(tMap, uv).g;
    float blue = texture2D(tMap, uv - distortion * 0.1).b;
    gl_FragColor = vec4(red, green, blue, 1.0);
  }
`;

interface HeroImageSceneProps {
  imageSrc: string;
}

export function HeroImageScene({ imageSrc }: HeroImageSceneProps) {
  const rootRef = useRef<HTMLDivElement>(null);
  const pointerRef = useRef({ x: 0.5, y: 0.5 });
  const velocityRef = useRef({ x: 0, y: 0 });
  const strengthRef = useRef(0);
  const lastMoveTimeRef = useRef(0);

  useEffect(() => {
    const root = rootRef.current;
    if (!root) return;
    const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
    if (reduceMotion.matches) return;

    const renderer = new Renderer({
      alpha: true,
      antialias: true,
      dpr: Math.min(window.devicePixelRatio, 2),
    });
    const gl = renderer.gl;
    root.appendChild(gl.canvas);

    gl.canvas.className = "absolute inset-0 h-full w-full";
    gl.clearColor(0, 0, 0, 0);

    const texture = new Texture(gl, {
      generateMipmaps: false,
      minFilter: gl.LINEAR,
      magFilter: gl.LINEAR,
    });

    const image = new Image();
    image.src = imageSrc;
    image.onload = () => {
      texture.image = image;
      texture.needsUpdate = true;
    };

    const program = new Program(gl, {
      vertex,
      fragment,
      uniforms: {
        tMap: { value: texture },
        uResolution: { value: [1, 1] },
        uImageResolution: { value: [1883, 855] },
        uPointer: { value: [0.5, 0.5] },
        uVelocity: { value: [0, 0] },
        uStrength: { value: 0 },
      },
      transparent: true,
    });

    const mesh = new Mesh(gl, {
      geometry: new Plane(gl, { width: 2, height: 2 }),
      program,
    });

    let animationFrame = 0;
    let width = 1;
    let height = 1;

    const resize = () => {
      const rect = root.getBoundingClientRect();
      width = Math.max(1, rect.width);
      height = Math.max(1, rect.height);
      renderer.setSize(width, height);
      program.uniforms.uResolution.value = [width, height];
    };

    const onPointerMove = (event: PointerEvent) => {
      const rect = root.getBoundingClientRect();
      const isInside =
        event.clientX >= rect.left &&
        event.clientX <= rect.right &&
        event.clientY >= rect.top &&
        event.clientY <= rect.bottom;

      if (!isInside) {
        velocityRef.current = { x: 0, y: 0 };
        return;
      }

      const nextPointer = {
        x: (event.clientX - rect.left) / rect.width,
        y: 1 - (event.clientY - rect.top) / rect.height,
      };
      const now = performance.now();
      const elapsed = Math.max(16, now - lastMoveTimeRef.current);
      lastMoveTimeRef.current = now;
      velocityRef.current = {
        x: ((nextPointer.x - pointerRef.current.x) / elapsed) * 16.67,
        y: ((nextPointer.y - pointerRef.current.y) / elapsed) * 16.67,
      };
      pointerRef.current = nextPointer;
      strengthRef.current = Math.min(1, strengthRef.current + 0.72);
    };

    const onPointerLeave = () => {
      velocityRef.current = { x: 0, y: 0 };
    };

    const render = () => {
      strengthRef.current *= 0.86;
      velocityRef.current = {
        x: velocityRef.current.x * 0.74,
        y: velocityRef.current.y * 0.74,
      };

      program.uniforms.uPointer.value = [
        pointerRef.current.x,
        pointerRef.current.y,
      ];
      program.uniforms.uVelocity.value = [
        velocityRef.current.x,
        velocityRef.current.y,
      ];
      program.uniforms.uStrength.value = strengthRef.current;
      renderer.render({ scene: mesh });
      animationFrame = requestAnimationFrame(render);
    };

    const observer = new ResizeObserver(resize);
    observer.observe(root);
    window.addEventListener("pointermove", onPointerMove);
    root.addEventListener("pointerleave", onPointerLeave);

    resize();
    animationFrame = requestAnimationFrame(render);

    return () => {
      cancelAnimationFrame(animationFrame);
      observer.disconnect();
      window.removeEventListener("pointermove", onPointerMove);
      root.removeEventListener("pointerleave", onPointerLeave);
      gl.canvas.remove();
    };
  }, [imageSrc]);

  return (
    <div
      ref={rootRef}
      aria-hidden="true"
      className="hero-ogl-scene absolute inset-[-18px]"
    >
      <div
        className="absolute inset-0 bg-cover bg-center"
        style={{ backgroundImage: `url(${imageSrc})` }}
      />
    </div>
  );
}
