// Aurora — ported from React Bits (reactbits.dev) to plain JSX. A soft band of light across
// the top of the three cream page heroes (services, about, clinic). Not the home hero, which
// is a full-bleed photograph and needs no help.
//
// It needs `ogl`, the same ESM-only library the backdrop and the hero rays use; every page
// already loads it as a module and hands it over on `window.OGL` with a `ps:ogl` event. This
// waits for that event rather than assuming it has landed, because module scripts are
// deferred and React usually mounts first.
//
// Deviations from upstream, all deliberate:
//
//   * **Upstream's `lightMode` branch is not used.** It outputs an opaque white-to-chroma
//     mix, which is right for a white page and wrong here: it would paint a white rectangle
//     on a cream slab. The normal premultiplied-alpha path composites over whatever is
//     behind, which is what a band on a coloured slab needs.
//   * Colours come from the site's own tokens and are re-read when the theme flips, the same
//     arrangement PageBackdrop uses.
//   * It renders nothing under prefers-reduced-motion.
//   * The frame loop only runs while the band is on screen. Every page already has one WebGL
//     context for the backdrop; this is a second, and browsers cap the number live at ~16.
//     An aurora animating below the fold is a context and a frame budget spent on nothing.
const AURORA_VERT = `#version 300 es
in vec2 position;
void main() { gl_Position = vec4(position, 0.0, 1.0); }
`;

const AURORA_FRAG = `#version 300 es
precision highp float;

uniform float uTime;
uniform float uAmplitude;
uniform vec3 uColorStops[3];
uniform vec2 uResolution;
uniform float uBlend;

out vec4 fragColor;

vec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); }

float snoise(vec2 v){
  const vec4 C = vec4(0.211324865405187, 0.366025403784439,
                      -0.577350269189626, 0.024390243902439);
  vec2 i  = floor(v + dot(v, C.yy));
  vec2 x0 = v - i + dot(i, C.xx);
  vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
  vec4 x12 = x0.xyxy + C.xxzz;
  x12.xy -= i1;
  i = mod(i, 289.0);
  vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
  vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
  m = m * m; m = m * m;
  vec3 x = 2.0 * fract(p * C.www) - 1.0;
  vec3 h = abs(x) - 0.5;
  vec3 ox = floor(x + 0.5);
  vec3 a0 = x - ox;
  m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
  vec3 g;
  g.x  = a0.x  * x0.x  + h.x  * x0.y;
  g.yz = a0.yz * x12.xz + h.yz * x12.yw;
  return 130.0 * dot(m, g);
}

struct ColorStop { vec3 color; float position; };

#define COLOR_RAMP(colors, factor, finalColor) {                 \\
  int index = 0;                                                 \\
  for (int i = 0; i < 2; i++) {                                  \\
     ColorStop currentColor = colors[i];                         \\
     bool isInBetween = currentColor.position <= factor;         \\
     index = int(mix(float(index), float(i), float(isInBetween)));\\
  }                                                              \\
  ColorStop currentColor = colors[index];                        \\
  ColorStop nextColor = colors[index + 1];                       \\
  float range = nextColor.position - currentColor.position;      \\
  float lerpFactor = (factor - currentColor.position) / range;   \\
  finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \\
}

void main() {
  vec2 uv = gl_FragCoord.xy / uResolution;

  ColorStop colors[3];
  colors[0] = ColorStop(uColorStops[0], 0.0);
  colors[1] = ColorStop(uColorStops[1], 0.5);
  colors[2] = ColorStop(uColorStops[2], 1.0);

  vec3 rampColor;
  COLOR_RAMP(colors, uv.x, rampColor);

  float height = snoise(vec2(uv.x * 2.0 + uTime * 0.1, uTime * 0.25)) * 0.5 * uAmplitude;
  height = exp(height);
  height = (uv.y * 2.0 - height + 0.2);
  float intensity = 0.6 * height;

  float midPoint = 0.20;
  float auroraAlpha = smoothstep(midPoint - uBlend * 0.5, midPoint + uBlend * 0.5, intensity);

  vec3 auroraColor = intensity * rampColor;
  fragColor = vec4(auroraColor * auroraAlpha, auroraAlpha);
}
`;

const Aurora = ({
  colorStops = ["#a8c8c2", "#68a29b", "#d97757"],
  amplitude = 0.9,
  blend = 0.62,
  speed = 0.45,
  className = "",
}) => {
  const { useRef, useEffect, useState } = React;
  const hostRef = useRef(null);
  const propsRef = useRef({ colorStops, amplitude, blend, speed });
  propsRef.current = { colorStops, amplitude, blend, speed };

  const [oglReady, setOglReady] = useState(() => !!window.OGL);
  const reduced = window.matchMedia
    && window.matchMedia("(prefers-reduced-motion: reduce)").matches;

  useEffect(() => {
    if (window.OGL) { setOglReady(true); return; }
    const onReady = () => setOglReady(true);
    window.addEventListener("ps:ogl", onReady);
    const poll = setInterval(() => { if (window.OGL) { setOglReady(true); clearInterval(poll); } }, 150);
    return () => { window.removeEventListener("ps:ogl", onReady); clearInterval(poll); };
  }, []);

  useEffect(() => {
    const ctn = hostRef.current;
    if (!ctn || !oglReady || reduced) return;
    const { Renderer, Program, Mesh, Color, Triangle } = window.OGL;

    const renderer = new Renderer({ alpha: true, premultipliedAlpha: true, antialias: true,
                                    dpr: Math.min(window.devicePixelRatio || 1, 1.5) });
    const gl = renderer.gl;
    gl.clearColor(0, 0, 0, 0);
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    gl.canvas.style.cssText = "width:100%;height:100%;display:block";

    const geometry = new Triangle(gl);
    if (geometry.attributes.uv) delete geometry.attributes.uv;

    const toRGB = (list) => list.map((hex) => { const c = new Color(hex); return [c.r, c.g, c.b]; });

    const program = new Program(gl, {
      vertex: AURORA_VERT,
      fragment: AURORA_FRAG,
      uniforms: {
        uTime: { value: 0 },
        uAmplitude: { value: amplitude },
        uColorStops: { value: toRGB(colorStops) },
        uResolution: { value: [1, 1] },
        uBlend: { value: blend },
      },
    });
    const mesh = new Mesh(gl, { geometry, program });
    ctn.appendChild(gl.canvas);

    const resize = () => {
      const w = ctn.offsetWidth, h = ctn.offsetHeight;
      if (!w || !h) return;
      renderer.setSize(w, h);
      program.uniforms.uResolution.value = [gl.drawingBufferWidth, gl.drawingBufferHeight];
      renderer.render({ scene: mesh });
    };
    const ro = new ResizeObserver(resize);
    ro.observe(ctn);
    resize();

    let raf = 0;
    let visible = true;
    const update = (t) => {
      raf = requestAnimationFrame(update);
      const p = propsRef.current;
      program.uniforms.uTime.value = t * 0.001 * (p.speed ?? speed);
      program.uniforms.uAmplitude.value = p.amplitude ?? amplitude;
      program.uniforms.uBlend.value = p.blend ?? blend;
      program.uniforms.uColorStops.value = toRGB(p.colorStops ?? colorStops);
      renderer.render({ scene: mesh });
    };
    const start = () => { if (visible && !raf) raf = requestAnimationFrame(update); };
    const stop = () => { if (raf) { cancelAnimationFrame(raf); raf = 0; } };

    const io = typeof IntersectionObserver === "function"
      ? new IntersectionObserver((es) => { visible = es.some((e) => e.isIntersecting); visible ? start() : stop(); },
                                 { rootMargin: "80px" })
      : null;
    if (io) io.observe(ctn); else start();
    const onVis = () => { document.hidden ? stop() : start(); };
    document.addEventListener("visibilitychange", onVis);
    start();

    return () => {
      stop();
      ro.disconnect();
      if (io) io.disconnect();
      document.removeEventListener("visibilitychange", onVis);
      try { ctn.removeChild(gl.canvas); } catch (e) { /* already gone */ }
      const lose = gl.getExtension("WEBGL_lose_context");
      if (lose) lose.loseContext();
    };
  }, [oglReady, reduced, amplitude]);

  if (reduced) return null;
  return <div ref={hostRef} className={`aurora ${className}`.trim()} aria-hidden="true" />;
};
window.Aurora = Aurora;
