/**
 * Aura fragment shader — faithful port of LiveKit's AgentAudioVisualizerAura.
 *
 * Uses 36 iterations of turbulence-warped SDF evaluation to produce a
 * luminous ring through accumulation. The ring shape emerges naturally
 * from the turbulence + circle SDF interaction — not from drawing a
 * ring directly.
 *
 * Uniforms (all from Aura component):
 *   u_color       vec3  Base RGB colour (0-1)
 *   u_colorShift  float HSV hue shift range across iterations (0-1)
 *   u_speed       float Animation speed multiplier
 *   u_amplitude   float Turbulence displacement strength
 *   u_frequency   float Wave complexity (remapped to 2.0-15.0)
 *   u_scale       float Circle SDF radius
 *   u_blur        float Edge softness
 *   u_brightness  float Overall brightness/opacity
 *   u_bloom       float Glow intensity (dark mode only)
 *   u_spacing     float Iteration spacing (remapped to 1.0-TAU)
 *   u_variance    float Amplitude variance per turbulence layer
 *   u_smoothing   float Dynamic blur smoothing factor
 *   u_lightMode   float 0.0 = dark mode, 1.0 = light mode
 *
 * Built-in from ShaderCanvas:
 *   iTime          float Elapsed seconds
 *   iResolution    vec2  Viewport pixel dimensions
 */
const auraFragmentShader = `#version 300 es
precision highp float;

uniform float iTime;
uniform vec2  iResolution;

uniform vec3  u_color;
uniform float u_colorShift;
uniform float u_speed;
uniform float u_amplitude;
uniform float u_frequency;
uniform float u_scale;
uniform float u_blur;
uniform float u_brightness;
uniform float u_bloom;
uniform float u_spacing;
uniform float u_variance;
uniform float u_smoothing;
uniform float u_lightMode;

out vec4 fragColor;

/* ── Constants ─────────────────────────────────────────────────────────────── */

const float TAU = 6.283185;

/* ── Utilities ─────────────────────────────────────────────────────────────── */

vec2 randFibo(vec2 p) {
  p = fract(p * vec2(443.897, 441.423));
  p += dot(p, p.yx + 19.19);
  return fract((p.xx + p.yx) * p.xy);
}

vec3 Tonemap(vec3 x) {
  x *= 4.0;
  return x / (1.0 + x);
}

float luma(vec3 color) {
  return dot(color, vec3(0.299, 0.587, 0.114));
}

vec3 rgb2hsv(vec3 c) {
  vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
  vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
  vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
  float d = q.x - min(q.w, q.y);
  float e = 1.0e-10;
  return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c) {
  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

/* ── SDF ───────────────────────────────────────────────────────────────────── */

float sdCircle(vec2 st, float r) {
  return length(st) - r;
}

/* ── Turbulence — 4 layers of sinusoidal displacement with rotation ────────── */

vec2 turb(vec2 pos, float t, float it) {
  mat2 rotation = mat2(0.6, -0.25, 0.25, 0.9);
  mat2 layerRotation = mat2(0.6, -0.8, 0.8, 0.6);

  float frequency = mix(2.0, 15.0, u_frequency);
  float amplitude = u_amplitude;
  float frequencyGrowth = 1.4;
  float animTime = t * 0.1 * u_speed;

  const int LAYERS = 4;
  for (int i = 0; i < LAYERS; i++) {
    vec2 rotatedPos = pos * rotation;
    vec2 wave = sin(frequency * rotatedPos + float(i) * animTime + it);

    pos += (amplitude / frequency) * rotation[0] * wave;

    rotation *= layerRotation;
    amplitude *= mix(1.0, max(wave.x, wave.y), u_variance);
    frequency *= frequencyGrowth;
  }

  return pos;
}

/* ── Main ──────────────────────────────────────────────────────────────────── */

const float ITERATIONS = 36.0;

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

  vec3 pp = vec3(0.0);
  vec3 bloom = vec3(0.0);
  float t = iTime * 0.5;
  vec2 pos = uv - 0.5;

  vec2 prevPos = turb(pos, t, 0.0 - 1.0 / ITERATIONS);
  float spacing = mix(1.0, TAU, u_spacing);

  for (float i = 1.0; i < ITERATIONS + 1.0; i++) {
    float iter = i / ITERATIONS;
    vec2 st = turb(pos, t, iter * spacing);
    float d = abs(sdCircle(st, u_scale));
    float pd = distance(st, prevPos);
    prevPos = st;
    float dynamicBlur = exp2(pd * 2.0 * 1.4426950408889634) - 1.0;
    float ds = smoothstep(0.0, u_blur * 0.05 + max(dynamicBlur * u_smoothing, 0.001), d);

    vec3 col = u_color;
    if (u_colorShift > 0.01) {
      vec3 hsv = rgb2hsv(col);
      hsv.x = fract(hsv.x + (1.0 - iter) * u_colorShift * 0.3);
      col = hsv2rgb(hsv);
    }

    float invd = 1.0 / max(d + dynamicBlur, 0.001);
    pp += (ds - 1.0) * col;
    bloom += clamp(invd, 0.0, 250.0) * col;
  }

  pp *= 1.0 / ITERATIONS;

  vec3 color;

  // Dark mode (default)
  if (u_lightMode < 0.5) {
    bloom = bloom / (bloom + 2e4);
    color = (-pp + bloom * 3.0 * u_bloom) * 1.2;
    color += (randFibo(gl_FragCoord.xy).x - 0.5) / 255.0;
    color = Tonemap(color);
    float alpha = luma(color) * u_brightness;
    fragColor = vec4(color * u_brightness, alpha);
  }
  // Light mode
  else {
    color = -pp;
    color += (randFibo(gl_FragCoord.xy).x - 0.5) / 255.0;

    float brightness = length(color);
    vec3 direction = brightness > 0.0 ? color / brightness : color;

    float factor = 2.0;
    float mappedBrightness = (brightness * factor) / (1.0 + brightness * factor);
    color = direction * mappedBrightness;

    float gray = dot(color, vec3(0.2, 0.5, 0.1));
    float saturationBoost = 3.0;
    color = mix(vec3(gray), color, saturationBoost);

    color = clamp(color, 0.0, 1.0);

    float alpha = mappedBrightness * clamp(u_brightness, 1.0, 2.0);
    fragColor = vec4(color, alpha);
  }
}
`;

export { auraFragmentShader };
