<template>
  <canvas ref="canvas" class="webgl-bg" :class="{ ready }"></canvas>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';

const canvas = ref<HTMLCanvasElement>();
const ready = ref(false);
let ctx: WebGLRenderingContext | null = null;
let animationId: number;
let resizeHandler: (() => void) | null = null;

const vertexShader = `
  attribute vec2 a_position;
  uniform float u_aspect;
  void main() {
    vec2 pos = a_position;
    pos.x /= u_aspect;
    gl_Position = vec4(pos, 0, 1);
    gl_PointSize = 7.0;
  }
`;

const fragmentShader = `
  precision mediump float;
  uniform vec3 u_color;
  void main() {
    float d = distance(gl_PointCoord, vec2(0.5));
    if (d > 0.5) discard;
    float alpha = (0.5 - d) * 2.0;
    gl_FragColor = vec4(u_color, alpha * 0.9);
  }
`;

function createShader(gl: WebGLRenderingContext, type: number, source: string) {
  const shader = gl.createShader(type);
  if (!shader) return null;
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.error(gl.getShaderInfoLog(shader));
    gl.deleteShader(shader);
    return null;
  }
  return shader;
}

function createProgram(gl: WebGLRenderingContext, vs: WebGLShader, fs: WebGLShader) {
  const program = gl.createProgram();
  if (!program) return null;
  gl.attachShader(program, vs);
  gl.attachShader(program, fs);
  gl.linkProgram(program);
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.error(gl.getProgramInfoLog(program));
    return null;
  }
  return program;
}

onMounted(() => {
  const c = canvas.value;
  if (!c) return;

  resizeHandler = () => {
    c.width = window.innerWidth;
    c.height = window.innerHeight;
  };
  resizeHandler();
  window.addEventListener('resize', resizeHandler);

  ctx = c.getContext('webgl', { alpha: true, premultipliedAlpha: false });
  if (!ctx) return;

  const gl = ctx;
  // Immediately clear to transparent — prevents white buffer flash
  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  const vs = createShader(gl, gl.VERTEX_SHADER, vertexShader);
  const fs = createShader(gl, gl.FRAGMENT_SHADER, fragmentShader);
  if (!vs || !fs) return;

  const program = createProgram(gl, vs, fs);
  if (!program) return;

  const positionLoc = gl.getAttribLocation(program, 'a_position');
  const colorLoc = gl.getUniformLocation(program, 'u_color');
  const aspectLoc = gl.getUniformLocation(program, 'u_aspect');

  // Particles
  const particleCount = 150;
  const positions = new Float32Array(particleCount * 2);
  const velocities = new Float32Array(particleCount * 2);

  const initAspect = c.width / c.height;
  for (let i = 0; i < particleCount; i++) {
    positions[i * 2] = (Math.random() * 2 - 1) * initAspect;
    positions[i * 2 + 1] = (Math.random() * 2 - 1);
    velocities[i * 2] = (Math.random() - 0.5) * 0.0006;
    velocities[i * 2 + 1] = (Math.random() - 0.5) * 0.0006;
  }

  const buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.enableVertexAttribArray(positionLoc);
  gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);

  gl.useProgram(program);
  // Read particle color from theme's --secondary CSS variable
  const cssColor = getComputedStyle(document.documentElement).getPropertyValue('--secondary').trim();
  let pr = 0.4, pg = 0.6, pb = 1.0; // fallback
  if (cssColor.startsWith('#') && cssColor.length >= 7) {
      pr = parseInt(cssColor.slice(1, 3), 16) / 255;
      pg = parseInt(cssColor.slice(3, 5), 16) / 255;
      pb = parseInt(cssColor.slice(5, 7), 16) / 255;
  }
  gl.uniform3f(colorLoc, pr, pg, pb);

  let frameCount = 0;
  function animate() {
    if (!ready.value && ++frameCount > 30) ready.value = true;
    gl.viewport(0, 0, c.width, c.height);
    gl.uniform1f(aspectLoc, c.width / c.height);
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Update positions
    const aspect = c.width / c.height;
    for (let i = 0; i < particleCount; i++) {
      positions[i * 2] += velocities[i * 2];
      positions[i * 2 + 1] += velocities[i * 2 + 1];

      // Wrap around (preserve offset to avoid stacking at edges)
      if (positions[i * 2] > aspect) positions[i * 2] -= 2 * aspect;
      if (positions[i * 2] < -aspect) positions[i * 2] += 2 * aspect;
      if (positions[i * 2 + 1] > 1) positions[i * 2 + 1] -= 2;
      if (positions[i * 2 + 1] < -1) positions[i * 2 + 1] += 2;
    }

    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.DYNAMIC_DRAW);
    gl.drawArrays(gl.POINTS, 0, particleCount);

    animationId = requestAnimationFrame(animate);
  }

  animate();
});

onUnmounted(() => {
  cancelAnimationFrame(animationId);
  if (resizeHandler) window.removeEventListener('resize', resizeHandler);
});
</script>

<style scoped>
.webgl-bg {
  position: fixed;
  top: 0;
  left: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: transparent;
}
.webgl-bg.ready {
  opacity: 1;
  transition: opacity 0.4s ease;
}
</style>