"use client";

/**
 * Iridescence — generative WebGL backdrop.
 *
 * A living interpretation of the brand wave: domain-warped fractal noise mapped
 * onto the brand spectrum (violet -> blue -> rose -> emerald) glowing out of a
 * near-black field, with a faint rainbow refraction band and animated film
 * grain. Drifts on its own and leans toward the cursor. Falls back to a static
 * dark CSS gradient when WebGL is unavailable or the user prefers reduced
 * motion.
 *
 * Renders behind everything (fixed, -z) and is purely decorative (aria-hidden).
 */

import { useEffect, useRef } from "react";
import { Renderer, Program, Mesh, Triangle, Vec2 } from "ogl";

const VERT = /* glsl */ `
  attribute vec2 position;
  void main() {
    gl_Position = vec4(position, 0.0, 1.0);
  }
`;

const FRAG = /* glsl */ `
  precision highp float;

  uniform float uTime;
  uniform vec2  uResolution;
  uniform vec2  uMouse;     // 0..1, smoothed
  uniform float uIntensity; // global motion amount (0 for reduced motion)

  // Brand palette anchors
  const vec3 VIOLET = vec3(0.545, 0.361, 0.965); // #8b5cf6
  const vec3 BLUE   = vec3(0.231, 0.510, 0.965); // #3b82f6
  const vec3 ROSE   = vec3(0.957, 0.475, 0.851); // soft pink
  const vec3 MINT   = vec3(0.204, 0.827, 0.600); // #34d399 emerald
  const vec3 BASE   = vec3(0.031, 0.031, 0.047); // near-black #08080c

  // -- hash / value noise --------------------------------------------------
  vec2 hash22(vec2 p) {
    p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
    return fract(sin(p) * 43758.5453123);
  }

  float noise(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    vec2 u = f * f * (3.0 - 2.0 * f);
    float a = dot(hash22(i + vec2(0.0, 0.0)) - 0.5, f - vec2(0.0, 0.0));
    float b = dot(hash22(i + vec2(1.0, 0.0)) - 0.5, f - vec2(1.0, 0.0));
    float c = dot(hash22(i + vec2(0.0, 1.0)) - 0.5, f - vec2(0.0, 1.0));
    float d = dot(hash22(i + vec2(1.0, 1.0)) - 0.5, f - vec2(1.0, 1.0));
    return 0.5 + mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
  }

  float fbm(vec2 p) {
    float v = 0.0;
    float amp = 0.55;
    mat2 rot = mat2(0.8, -0.6, 0.6, 0.8);
    for (int i = 0; i < 5; i++) {
      v += amp * noise(p);
      p = rot * p * 2.0;
      amp *= 0.5;
    }
    return v;
  }

  // film grain
  float grain(vec2 uv, float t) {
    return fract(sin(dot(uv * (t + 1.0), vec2(12.9898, 78.233))) * 43758.5453);
  }

  void main() {
    vec2 uv = gl_FragCoord.xy / uResolution.xy;
    float aspect = uResolution.x / uResolution.y;
    vec2 p = uv;
    p.x *= aspect;

    float t = uTime * 0.045 * (0.25 + uIntensity);

    // mouse parallax (gentle)
    vec2 m = (uMouse - 0.5);
    p += m * 0.12 * uIntensity;

    // domain warping for that liquid cloud feel
    vec2 q = vec2(fbm(p + vec2(0.0, t)), fbm(p + vec2(5.2, -t)));
    vec2 r = vec2(
      fbm(p + 1.7 * q + vec2(8.3, 2.8) + 0.15 * t),
      fbm(p + 1.7 * q + vec2(1.2, 6.5) - 0.12 * t)
    );
    float f = fbm(p + 2.0 * r);

    // the brand spectrum, glowing ADDITIVELY out of a near-black base
    vec3 glow = vec3(0.0);
    glow += VIOLET * smoothstep(0.35, 0.95, f);
    glow += BLUE   * smoothstep(0.45, 1.0, r.x) * 0.9;
    glow += ROSE   * smoothstep(0.62, 1.05, q.y) * 0.5;
    glow += MINT   * smoothstep(0.66, 1.0, r.y) * 0.55;

    // concentrate the light into a soft diagonal wave band (echoes the asset)
    float band = smoothstep(0.62, 0.0, abs((uv.x - uv.y) * 1.25 - 0.05 + 0.06 * sin(t * 2.0)));
    float field = smoothstep(0.2, 0.85, f);
    float energy = (0.35 + 0.65 * band) * field;

    vec3 col = BASE + glow * energy * (0.55 + 0.45 * uIntensity);

    // faint rainbow refraction along the band
    vec3 spectrum = 0.5 + 0.5 * cos(6.2831 * (vec3(0.0, 0.33, 0.67) + (uv.x + uv.y) * 0.7));
    col += spectrum * band * 0.05 * (0.4 + uIntensity);

    // darken edges so content stays the focus
    float vig = smoothstep(1.3, 0.2, distance(uv, vec2(0.5)));
    col *= 0.55 + 0.45 * vig;

    // keep the base from washing out; clamp the glow softly
    col = max(col, BASE * 0.6);

    // animated grain to kill banding + add texture
    float g = grain(uv, floor(uTime * 12.0) * 0.5);
    col += (g - 0.5) * 0.03;

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

export default function Iridescence() {
  const ref = useRef<HTMLCanvasElement>(null);

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

    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    let renderer: Renderer;
    try {
      renderer = new Renderer({
        canvas,
        dpr: Math.min(window.devicePixelRatio, 1.75),
        alpha: false,
        antialias: false,
      });
    } catch {
      // WebGL unavailable — CSS fallback (set on the host) remains visible.
      canvas.style.display = "none";
      return;
    }

    const gl = renderer.gl;
    gl.clearColor(0.031, 0.031, 0.047, 1);

    const uniforms = {
      uTime: { value: 0 },
      uResolution: { value: new Vec2(1, 1) },
      uMouse: { value: new Vec2(0.5, 0.5) },
      uIntensity: { value: reduce ? 0 : 1 },
    };

    const program = new Program(gl, {
      vertex: VERT,
      fragment: FRAG,
      uniforms,
    });
    const mesh = new Mesh(gl, { geometry: new Triangle(gl), program });

    const resize = () => {
      const w = window.innerWidth;
      const h = window.innerHeight;
      renderer.setSize(w, h);
      uniforms.uResolution.value.set(gl.drawingBufferWidth, gl.drawingBufferHeight);
    };
    resize();
    window.addEventListener("resize", resize);

    // smoothed pointer
    const target = { x: 0.5, y: 0.5 };
    const onMove = (e: PointerEvent) => {
      target.x = e.clientX / window.innerWidth;
      target.y = 1 - e.clientY / window.innerHeight;
    };
    window.addEventListener("pointermove", onMove, { passive: true });

    let raf = 0;
    let running = true;
    const start = performance.now();

    const loop = (now: number) => {
      if (!running) return;
      raf = requestAnimationFrame(loop);
      uniforms.uTime.value = (now - start) / 1000;
      // ease pointer
      const m = uniforms.uMouse.value;
      m.x += (target.x - m.x) * 0.04;
      m.y += (target.y - m.y) * 0.04;
      renderer.render({ scene: mesh });
    };

    if (reduce) {
      // render a single frame, then idle
      uniforms.uTime.value = 18;
      renderer.render({ scene: mesh });
    } else {
      raf = requestAnimationFrame(loop);
    }

    // pause when tab hidden (perf + battery)
    const onVis = () => {
      if (document.hidden) {
        running = false;
        cancelAnimationFrame(raf);
      } else if (!reduce) {
        running = true;
        raf = requestAnimationFrame(loop);
      }
    };
    document.addEventListener("visibilitychange", onVis);

    return () => {
      running = false;
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", resize);
      window.removeEventListener("pointermove", onMove);
      document.removeEventListener("visibilitychange", onVis);
      const ext = gl.getExtension("WEBGL_lose_context");
      ext?.loseContext();
    };
  }, []);

  return (
    <div className="iridescence" aria-hidden="true">
      <canvas ref={ref} className="iridescence__canvas" />
    </div>
  );
}
