/* =====================================================================
   HeroShader — bespoke per-tod WebGL backgrounds.

   NIGHT   — quiet deep-field starscape + slow drifting aurora veil.
             Cursor warms the nearest stars and gently lifts the aurora.
             No disc, no ring, no rainbow anywhere.
   SUNRISE — autonomous drifting sun with warped volumetric rays, plus
             a cursor-reactive lens flare that sweeps a warm streak
             through the sky. That's the flair.
   DAY     — slow flowing warm field. (unchanged)
   SUNSET  — molten caustics with a SPARSE, SLOW layer of fireflies that
             drift and blink. Fewer than before, more firefly than sparkle.
   ===================================================================== */

const { useEffect: useEffectHs, useRef: useRefHs } = React;

/* --------------------------------------------------------------------
   1. NIGHT — starfield + subtle aurora veil
   -------------------------------------------------------------------- */
const WARP_FRAG = `
precision highp float;
uniform vec2 uRes; uniform float uTime; uniform vec2 uMouse;

float hash(vec2 p){ return fract(sin(dot(p, vec2(23.1, 47.7))) * 43758.5); }
float noise(vec2 p){ vec2 i=floor(p), f=fract(p); f=f*f*(3.-2.*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);}
float fbm(vec2 p){ float v=0., a=0.5; for(int i=0;i<5;i++){ v+=a*noise(p); p=p*2.02+vec2(1.3,-0.7); a*=0.5;} return v;}

vec3 starLayer(vec2 uv, float scale, vec2 drift, vec2 parOff, float bright, float t){
  vec2 p = uv * scale + drift + parOff;
  vec2 cell = floor(p);
  vec2 f = fract(p) - 0.5;
  float h = hash(cell);
  if (h < 0.78) return vec3(0.0);
  vec2 pos = vec2(hash(cell + 11.0), hash(cell + 29.0)) - 0.5;
  pos *= 0.6;
  float r = length(f - pos);
  float pt = exp(-r * 90.0) + exp(-r * 22.0) * 0.08;
  float tw = 0.72 + 0.28 * sin(t * (0.35 + hash(cell + 3.0) * 0.9) + hash(cell) * 6.28);
  float cc = hash(cell + 7.0);
  vec3 tint = (cc < 0.85) ? vec3(0.92, 0.96, 1.08) : vec3(1.08, 0.95, 0.78);
  return tint * pt * tw * bright * pow(h, 1.6);
}

void main(){
  vec2 uv = (gl_FragCoord.xy * 2.0 - uRes) / min(uRes.x, uRes.y);
  vec2 uv01 = gl_FragCoord.xy / uRes;
  vec2 m  = (uMouse * 2.0 - 1.0);
  vec2 toM = m - uv;
  float dM = length(toM);
  float t = uTime;

  /* Gentle pull — stars nudge toward cursor */
  float pull = 0.03 * exp(-dM * 1.3);
  vec2 warp = normalize(toM + 1e-4) * pull;
  vec2 suv = uv + warp;
  vec2 mShift = m * 0.015;

  vec3 col = vec3(0.0);

  /* Deep-space gradient — cool dusk sky, no rainbow */
  vec3 bgTop = vec3(0.006, 0.010, 0.024);
  vec3 bgBot = vec3(0.014, 0.020, 0.046);
  vec3 bg = mix(bgBot, bgTop, smoothstep(-1.0, 1.0, uv.y));
  col += bg;

  /* Aurora veil — very slow, low-contrast horizontal ribbon drifting
     across the upper half of the sky. Responds subtly to cursor Y. */
  float auroraY = 0.55 + sin(t * 0.08) * 0.08 + (uMouse.y - 0.5) * 0.05;
  float bandDist = abs(uv01.y - auroraY);
  float auroraShape = exp(-bandDist * 8.0);
  /* warp the ribbon with noise so it looks like curtain folds */
  float curtain = fbm(vec2(uv01.x * 3.0 - t * 0.03, uv01.y * 2.0 + t * 0.02));
  auroraShape *= 0.35 + 0.9 * curtain;
  /* cool cyan → indigo aurora, never warm, never rainbow */
  vec3 auroraLo = vec3(0.10, 0.26, 0.42);
  vec3 auroraHi = vec3(0.18, 0.40, 0.55);
  vec3 aurora = mix(auroraLo, auroraHi, curtain);
  col += aurora * auroraShape * 0.10;

  /* Stars — three parallax layers */
  col += starLayer(suv, 26.0, vec2(t * 0.004, 0.0), mShift * 0.3, 0.55, t);
  col += starLayer(suv, 14.0, vec2(-t * 0.006, t * 0.002), mShift * 0.7, 0.85, t);
  col += starLayer(suv, 7.5,  vec2(t * 0.008, -t * 0.003), mShift * 1.4, 1.1, t);

  /* Cursor: very subtle cool moonlight pool — no disc, no ring */
  float pool = exp(-dM * 1.7) * 0.07 + exp(-dM * 4.0) * 0.04;
  col += vec3(0.32, 0.48, 0.82) * pool;

  /* Brighten nearby stars slightly without tinting them */
  float nearBoost = exp(-dM * 2.2) * 0.55;
  col += col * nearBoost * 0.3;

  /* Outer vignette */
  col *= 1.0 - 0.18 * dot(uv, uv);

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

/* --------------------------------------------------------------------
   2. SUNRISE — autonomous sun + cursor-reactive lens-flare streak.
   -------------------------------------------------------------------- */
const SUNRISE_FRAG = `
precision highp float;
uniform vec2 uRes; uniform float uTime; uniform vec2 uMouse;

float hash(vec2 p){return fract(sin(dot(p,vec2(23.1,47.7)))*43758.5);}
float noise(vec2 p){vec2 i=floor(p),f=fract(p); f=f*f*(3.-2.*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);}
float fbm(vec2 p){float v=0.,a=0.55; for(int i=0;i<5;i++){v+=a*noise(p); p=p*2.02+vec2(1.3,-0.7); a*=0.5;} return v;}

void main(){
  vec2 uv  = gl_FragCoord.xy / uRes;
  vec2 cuv = (gl_FragCoord.xy * 2.0 - uRes) / min(uRes.x, uRes.y);
  vec2 m   = uMouse;
  vec2 mc  = (uMouse * 2.0 - 1.0);
  float t  = uTime * 0.16;

  /* Autonomous sun motion */
  vec2 sun = vec2(
    0.48 + sin(t * 0.6) * 0.12,
    0.18 + (sin(t * 0.4) * 0.5 + 0.5) * 0.06
  );

  vec2 toSun = uv - sun;
  float sunDist = length(toSun);

  /* Sky gradient */
  vec3 skyEmber = vec3(0.98, 0.48, 0.18);
  vec3 skyRose  = vec3(0.78, 0.22, 0.32);
  vec3 skyPlum  = vec3(0.28, 0.08, 0.22);
  vec3 skyInd   = vec3(0.06, 0.04, 0.14);
  vec3 col = mix(skyEmber, skyRose, smoothstep(0.10, 0.42, uv.y));
  col = mix(col, skyPlum, smoothstep(0.42, 0.72, uv.y));
  col = mix(col, skyInd,  smoothstep(0.72, 1.05, uv.y));

  /* Cursor warmth bloom */
  float toCursor = distance(uv, m);
  float warmth = exp(-toCursor * 2.2) * 0.22 + exp(-toCursor * 6.0) * 0.10;
  col += vec3(1.0, 0.55, 0.22) * warmth;

  /* Horizon ground band */
  float horizon = smoothstep(0.20, 0.14, uv.y);
  col = mix(col, vec3(0.05, 0.02, 0.07), horizon * 0.95);

  /* Autonomous volumetric rays with gentle bend toward cursor */
  float rayTwist = t * 0.4;
  float rays = 0.0;
  vec2 step = toSun * (1.0 / 28.0);
  float bendAmt = 0.12 * exp(-toCursor * 1.8);
  for (int i = 0; i < 28; i++) {
    vec2 sp = uv - step * float(i);
    float sa = atan(sp.y - sun.y, sp.x - sun.x);
    float warp = fbm(sp * 3.0 + vec2(t * 0.6, -t * 0.4)) * 0.9;
    vec2 spToM = m - sp;
    float bendAngle = atan(spToM.y, spToM.x);
    sa = mix(sa, bendAngle, bendAmt);
    float ray = 0.5 + 0.5 * sin((sa + warp) * 24.0 + rayTwist * 1.6);
    ray = pow(max(ray, 0.0), 4.0);
    float dust = fbm(sp * 5.5 + vec2(t * 0.8, t * 0.2));
    ray *= 0.5 + 0.9 * dust;
    float atten = exp(-length(sp - sun) * 2.2);
    rays += ray * atten * 0.065;
  }
  rays = clamp(rays, 0.0, 1.8);
  vec3 rayCol = mix(vec3(0.95, 0.38, 0.12), vec3(1.0, 0.88, 0.5), rays * 0.7);
  col += rayCol * rays;

  /* --- FLAIR: cursor-reactive lens-flare streak ---
     A soft anamorphic streak oriented along the sun→cursor axis,
     centered on the cursor. Only visible when cursor is in the sky. */
  vec2 sunToCursor = m - sun;
  float flareLen = length(sunToCursor);
  vec2 dir = sunToCursor / max(flareLen, 1e-4);
  vec2 perp = vec2(-dir.y, dir.x);
  vec2 rel = uv - m;
  float along = dot(rel, dir);
  float across = dot(rel, perp);
  /* Horizontal streak: wide along axis, very narrow across */
  float streak = exp(-abs(across) * 110.0) * exp(-abs(along) * 3.5);
  /* Gentle pulse so it breathes */
  float pulse = 0.7 + 0.3 * sin(uTime * 0.8);
  /* Skybound only — fades out near horizon */
  float skyMask = smoothstep(0.12, 0.25, m.y);
  col += vec3(1.0, 0.82, 0.5) * streak * 1.1 * pulse * skyMask;
  /* Tiny warm bloom where cursor is */
  float flareBloom = exp(-length(rel) * 9.0) * 0.35 * skyMask;
  col += vec3(1.0, 0.7, 0.35) * flareBloom;
  /* Ghost orb on the opposite side of the sun — classic lens flair */
  vec2 ghostPos = sun - sunToCursor * 0.45;
  float ghost = exp(-length(uv - ghostPos) * 22.0) * 0.28 * skyMask;
  col += vec3(1.0, 0.6, 0.8) * ghost;

  /* Sun disc + halo */
  float sunCore = exp(-sunDist * 40.0);
  float sunHalo = exp(-sunDist * 6.0);
  col += vec3(1.0, 0.93, 0.58) * sunCore * 1.7;
  col += vec3(1.0, 0.58, 0.24) * sunHalo * 0.38;

  /* Drifting dust motes, slight cursor attraction */
  float motes = 0.0;
  for (int k = 0; k < 3; k++) {
    float fk = float(k);
    vec2 mp = cuv * (2.2 + fk * 1.5) + vec2(t * (0.7 + fk * 0.3), -t * (0.5 + fk * 0.2));
    mp += (mc - cuv) * exp(-length(cuv - mc) * 1.8) * 0.18;
    motes += pow(fbm(mp), 3.5) * (0.35 - fk * 0.08);
  }
  col += vec3(0.35, 0.22, 0.1) * motes * (1.0 - horizon);

  col *= 1.0 - 0.2 * dot(cuv, cuv);
  gl_FragColor = vec4(col, 1.0);
}
`;

/* --------------------------------------------------------------------
   3. SUNSET — molten caustics with SPARSE, SLOW fireflies.
   -------------------------------------------------------------------- */
const SUNSET_FRAG = `
precision highp float;
uniform vec2 uRes; uniform float uTime; uniform vec2 uMouse;

vec2 hash2(vec2 p){ p = vec2(dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)));
  return -1.0 + 2.0 * fract(sin(p) * 43758.5453); }
float  hash1(vec2 p){ return fract(sin(dot(p, vec2(41.3, 289.1))) * 43758.5453); }

float worley(vec2 p){
  vec2 n = floor(p); vec2 f = fract(p);
  float md = 1.0;
  for (int j = -1; j <= 1; j++) {
    for (int i = -1; i <= 1; i++) {
      vec2 g = vec2(float(i), float(j));
      vec2 o = hash2(n + g);
      o = 0.5 + 0.5 * sin(uTime * 0.55 + 6.2831 * o);
      vec2 r = g + o - f;
      float d = dot(r, r);
      md = min(md, d);
    }
  }
  return sqrt(md);
}

/* Fireflies — sparser (smaller grid, higher threshold), slower drift,
   slower pulse. Subtle cursor attraction. More firefly than sparkle. */
float fireflies(vec2 uv, vec2 m){
  float acc = 0.0;
  for (int j = 0; j < 3; j++) {
    for (int i = 0; i < 5; i++) {
      vec2 cell = vec2(float(i), float(j));
      float h = hash1(cell * 13.37);
      /* Only ~35% of cells spawn a firefly */
      if (h < 0.65) continue;

      vec2 anchor = (cell + vec2(hash1(cell + 7.1), hash1(cell + 19.3))) / vec2(5.0, 3.0);
      anchor = anchor * 2.0 - 1.0;
      anchor.x *= uRes.x / uRes.y;

      /* Slow individual drift — halved from before */
      float sp = 0.12 + hash1(cell + 3.0) * 0.18;
      float ph = hash1(cell + 5.0) * 6.2831;
      vec2 drift = vec2(sin(uTime * sp + ph), cos(uTime * sp * 0.7 + ph * 0.5)) * 0.14;

      vec2 pos = anchor + drift;

      /* Attraction to cursor */
      vec2 toM = m - pos;
      float dM = length(toM);
      pos += normalize(toM + 1e-4) * 0.10 * exp(-dM * 1.3);

      /* Slower blink */
      float pulse = 0.5 + 0.5 * sin(uTime * (0.55 + hash1(cell + 9.0) * 0.7) + ph);

      float d = length(uv - pos);
      float core = exp(-d * 120.0);
      float halo = exp(-d * 24.0) * 0.20;
      acc += (core + halo) * (0.5 + 0.5 * pulse);
    }
  }
  return acc;
}

void main(){
  vec2 uv = (gl_FragCoord.xy * 2.0 - uRes) / min(uRes.x, uRes.y);
  vec2 m = (uMouse * 2.0 - 1.0);
  float t = uTime * 0.3;

  vec2 p = uv * 2.4;
  p += vec2(sin(p.y * 1.5 + t) * 0.35, cos(p.x * 1.2 - t * 0.8) * 0.35);
  p += (m - uv) * exp(-length(uv - m) * 1.0) * 0.4;

  float w1 = worley(p * 1.3);
  float w2 = worley(p * 3.0 + vec2(t * 0.5, -t * 0.3));

  float veins1 = 1.0 - smoothstep(0.02, 0.45, w1); veins1 = pow(veins1, 3.0);
  float veins2 = 1.0 - smoothstep(0.0, 0.35, w2);  veins2 = pow(veins2, 4.0);

  vec3 plum    = vec3(0.09, 0.02, 0.12);
  vec3 violet  = vec3(0.28, 0.05, 0.32);
  vec3 magenta = vec3(0.78, 0.10, 0.38);
  vec3 pink    = vec3(0.98, 0.32, 0.42);
  vec3 coral   = vec3(1.0,  0.45, 0.22);
  vec3 ember   = vec3(0.62, 0.10, 0.10);
  vec3 gold    = vec3(1.0,  0.72, 0.22);
  vec3 white   = vec3(1.0,  0.94, 0.74);

  vec3 col = plum;
  col = mix(col, violet,  smoothstep(0.9, 0.55, uv.y));
  col = mix(col, magenta, smoothstep(0.55, 0.15, uv.y));
  col = mix(col, pink,    smoothstep(0.15, -0.25, uv.y));
  col = mix(col, coral,   smoothstep(-0.25, -0.8, uv.y));

  float pulse = 0.5 + 0.5 * sin(t * 0.8 + uv.y * 1.8);
  col += ember * pulse * 0.15;

  col = mix(col, ember, veins1 * 0.55);
  col = mix(col, coral, veins1 * 0.45);
  col += gold * veins2 * 0.45;
  col += white * pow(veins2, 3.0) * 0.25;
  col += white * (veins1 * veins2) * 0.55;

  float magBand = exp(-pow((uv.y - 0.35) * 2.2, 2.0));
  col += magenta * magBand * 0.12 * (0.5 + pulse);

  /* Fireflies — sparser, warm, slow */
  float ff = fireflies(uv, m);
  vec3 ffCol = mix(vec3(1.0, 0.78, 0.32), vec3(1.0, 0.92, 0.6), clamp(ff, 0.0, 1.0));
  col += ffCol * ff * 1.1;

  /* Subtle cursor heat */
  col += gold * exp(-length(uv - m) * 3.0) * 0.16;

  col *= 1.0 - 0.22 * dot(uv, uv);

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

/* --------------------------------------------------------------------
   4. FLOW (day) — unchanged
   -------------------------------------------------------------------- */
const FLOW_FRAG = `
precision highp float;
uniform vec2 uRes; uniform float uTime; uniform vec2 uMouse;

float hash(vec2 p){return fract(sin(dot(p,vec2(23.1,47.7)))*43758.5);}
float noise(vec2 p){vec2 i=floor(p),f=fract(p); f=f*f*(3.-2.*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);}
float fbm(vec2 p){float v=0.,a=0.55; for(int i=0;i<5;i++){v+=a*noise(p); p=p*2.02+vec2(1.3,-0.7); a*=0.5;} return v;}

void main(){
  vec2 uv = (gl_FragCoord.xy * 2.0 - uRes) / min(uRes.x, uRes.y);
  vec2 m = (uMouse * 2.0 - 1.0);
  float t = uTime * 0.035;

  vec2 p = uv * 1.2;
  vec2 w = vec2(fbm(p * 0.35 + vec2(t*0.6, -t*0.4)), fbm(p * 0.35 + vec2(-t*0.5, t*0.7))) * 2.0 - 1.0;
  p += w * 1.1;
  p += normalize(m - uv + 0.0001) * exp(-distance(uv, m) * 1.1) * 0.3;

  float v = fbm(p * 0.6 + t * 0.35);
  v = smoothstep(0.15, 0.95, v);

  vec3 bg  = vec3(0.045, 0.050, 0.082);
  vec3 mid = vec3(0.14, 0.15, 0.22);
  vec3 acc = vec3(0.55, 0.58, 0.32);
  vec3 hot = vec3(0.97, 0.83, 0.28);

  vec3 col = mix(bg, mid, smoothstep(0.05, 0.55, v));
  col = mix(col, acc, smoothstep(0.55, 0.82, v));
  col = mix(col, hot, smoothstep(0.85, 1.0, v));
  col *= 1.0 - 0.28 * dot(uv, uv);

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

const TOD_TO_KIND = { night: 'warp', sunrise: 'sunrise', day: 'flow', sunset: 'sunset' };

function buildProgram(gl, fragSrc) {
  const vs = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(vs, 'attribute vec2 p; void main(){gl_Position=vec4(p,0.,1.);}');
  gl.compileShader(vs);
  const fs = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(fs, fragSrc);
  gl.compileShader(fs);
  if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
    console.warn('HeroShader frag compile:', gl.getShaderInfoLog(fs));
    return null;
  }
  const prog = gl.createProgram();
  gl.attachShader(prog, vs); gl.attachShader(prog, fs); gl.linkProgram(prog);
  return prog;
}

function HeroShader({ tod = 'night' }) {
  const containerRef = useRefHs(null);
  const canvasRef    = useRefHs(null);
  const todRef       = useRefHs(tod);

  useEffectHs(() => { todRef.current = tod; }, [tod]);

  useEffectHs(() => {
    const canvas    = canvasRef.current;
    const container = containerRef.current;
    if (!canvas || !container) return;

    const gl = canvas.getContext('webgl', { antialias: false, premultipliedAlpha: false });
    if (!gl) return;

    const fragByKind = { warp: WARP_FRAG, sunrise: SUNRISE_FRAG, sunset: SUNSET_FRAG, flow: FLOW_FRAG };

    const progs = {};
    Object.entries(fragByKind).forEach(([kind, src]) => {
      const prog = buildProgram(gl, src);
      if (!prog) return;
      progs[kind] = {
        prog,
        u: {
          pos:   gl.getAttribLocation(prog, 'p'),
          uRes:  gl.getUniformLocation(prog, 'uRes'),
          uTime: gl.getUniformLocation(prog, 'uTime'),
          uMouse:gl.getUniformLocation(prog, 'uMouse'),
        }
      };
    });

    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, 1,-1, 1,1]), gl.STATIC_DRAW);

    let raf, running = true;
    const t0 = performance.now();
    let mx = 0.5, my = 0.5, tmx = 0.5, tmy = 0.5;

    const resize = () => {
      const r = container.getBoundingClientRect();
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      canvas.width  = Math.max(1, Math.floor(r.width * dpr));
      canvas.height = Math.max(1, Math.floor(r.height * dpr));
      canvas.style.width  = r.width + 'px';
      canvas.style.height = r.height + 'px';
      gl.viewport(0, 0, canvas.width, canvas.height);
    };
    resize();
    const ro = new ResizeObserver(resize); ro.observe(container);

    const onMove = (e) => {
      const r = container.getBoundingClientRect();
      tmx = Math.max(0, Math.min(1, (e.clientX - r.left) / r.width));
      tmy = Math.max(0, Math.min(1, (e.clientY - r.top)  / r.height));
    };
    window.addEventListener('mousemove', onMove);

    const frame = () => {
      if (!running) return;
      const curTod = todRef.current;
      const k = curTod === 'night' ? 0.10 : 0.05;
      mx += (tmx - mx) * k;
      my += (tmy - my) * k;

      const kind = TOD_TO_KIND[curTod] || 'warp';
      const p = progs[kind];
      if (p) {
        gl.useProgram(p.prog);
        gl.bindBuffer(gl.ARRAY_BUFFER, buf);
        gl.enableVertexAttribArray(p.u.pos);
        gl.vertexAttribPointer(p.u.pos, 2, gl.FLOAT, false, 0, 0);
        gl.uniform2f(p.u.uRes, canvas.width, canvas.height);
        gl.uniform1f(p.u.uTime, (performance.now() - t0) * 0.001);
        gl.uniform2f(p.u.uMouse, mx, 1 - my);
        gl.drawArrays(gl.TRIANGLES, 0, 6);
      }
      raf = requestAnimationFrame(frame);
    };
    frame();

    return () => {
      running = false;
      cancelAnimationFrame(raf);
      ro.disconnect();
      window.removeEventListener('mousemove', onMove);
    };
  }, []);

  return (
    <div ref={containerRef} style={{ position: 'absolute', inset: 0, overflow: 'hidden', background: 'var(--bg)' }}>
      <canvas ref={canvasRef} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', display: 'block' }}/>
    </div>
  );
}

Object.assign(window, { HeroShader });
