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

const canvas = ref<HTMLCanvasElement | null>(null)
let ctx: CanvasRenderingContext2D | null = null
let animId: number
let particles: Particle[] = []
let accentColor: string = ''
let scrollY: number = 0
let mouseX: number = -9999
let mouseY: number = -9999
let bgParticles: Particle[] = []

interface Particle {
  x: number
  y: number
  vx: number
  vy: number
  size: number
  parallax: number
}

function parseAccent(hex: string): [number, number, number] {
  const r = parseInt(hex.slice(1, 3), 16)
  const g = parseInt(hex.slice(3, 5), 16)
  const b = parseInt(hex.slice(5, 7), 16)
  return [r, g, b]
}

function parseBgAccent(hex: string): [number, number, number] {
  const r = parseInt(hex.slice(1, 3), 16)
  const g = parseInt(hex.slice(3, 5), 16)
  const b = parseInt(hex.slice(5, 7), 16)
  return [r, g, b]
}

function createParticles(w: number, h: number): Particle[] {
  const count = Math.floor((w * h) / 30000)
  const capped = Math.min(count, 70)
  const particlesArr: Particle[] = []

  for (let i = 0; i < capped; i++) {
    particlesArr.push({
      x: Math.random() * w,
      y: Math.random() * h,
      vx: (Math.random() - 0.5) * 0.3,
      vy: (Math.random() - 0.5) * 0.3,
      size: 5,
      parallax: 0.15,
    })
  }

  return particlesArr
}

function createBgParticles(w: number, h: number): Particle[] {
  const count = Math.floor((w * h) / 8000)
  const capped = Math.min(count, 400)
  const particlesArr: Particle[] = []

  for (let i = 0; i < capped; i++) {
    particlesArr.push({
      x: Math.random() * w,
      y: Math.random() * h,
      vx: (Math.random() - 0.5) * 0.15,
      vy: (Math.random() - 0.5) * 0.15,
      size: 3,
      parallax: 0.03,
    })
  }

  return particlesArr
}

function drawConnections(w: number, h: number) {
  if (!ctx) return
  const [r, g, b] = parseAccent(accentColor)

  for (let i = 0; i < particles.length; i++) {
    const a = particles[i]
    for (let j = i + 1; j < particles.length; j++) {
      const b2 = particles[j]
      const ay = (a as any)._dy ?? a.y
      const by = (b2 as any)._dy ?? b2.y
      const dx = a.x - b2.x
      const dy = ay - by
      const dist = Math.sqrt(dx * dx + dy * dy)
      if (dist < 100) {
        const connAlpha = (1 - dist / 100) * 0.25
        const offset = Math.ceil(Math.max(a.size, b2.size))
        const angle = Math.atan2(dy, dx)
        const edgeA_x = a.x + Math.cos(angle) * offset
        const edgeA_y = ay + Math.sin(angle) * offset
        const edgeB_x = b2.x - Math.cos(angle) * offset
        const edgeB_y = by - Math.sin(angle) * offset
        ctx.beginPath()
        ctx.moveTo(edgeA_x, edgeA_y)
        ctx.lineTo(edgeB_x, edgeB_y)
        ctx.strokeStyle = `rgba(${r},${g},${b},${connAlpha})`
        ctx.lineWidth = 1
        ctx.stroke()
      }
    }
  }
}

function drawTuiParticle(x: number, y: number, size: number, r: number, g: number, b: number) {
  if (!ctx) return

  // Draw plus/cross shape (TUI cursor style)
  ctx.fillStyle = `rgba(${r},${g},${b},0.85)`
  ctx.fillRect(Math.round(x - size / 2), Math.round(y - size / 2), size, size)
}

function drawBgParticle(x: number, y: number, size: number, r: number, g: number, b: number) {
  if (!ctx) return

  ctx.fillStyle = `rgba(${r},${g},${b},0.7)`
  ctx.fillRect(Math.round(x - size / 2), Math.round(y - size / 2), size, size)
}

function animate(): void {
  if (!ctx || !canvas.value) return
  const w = canvas.value.width
  const h = canvas.value.height
  const [r, g, b] = parseAccent(accentColor)

  ctx.clearRect(0, 0, w, h)


  for (const p of particles) {
    const drawY = p.y - scrollY * p.parallax
    const dx = p.x - mouseX
    const dy = drawY - mouseY
    const dist2 = dx * dx + dy * dy
    if (dist2 < 6400 && dist2 > 1) {
      const dist = Math.sqrt(dist2)
      const force = 0.8 / dist
      p.vx += (dx / dist) * force
      p.vy += (dy / dist) * force
    }

    p.vx *= 0.98
    p.vy *= 0.98

    const minSpeed = 0.15
    const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy)
    if (speed < minSpeed) {
      const angle = Math.atan2((Math.random() - 0.5) * 0.3, (Math.random() - 0.5) * 0.3)
      p.vx = Math.cos(angle) * minSpeed
      p.vy = Math.sin(angle) * minSpeed
    }

    p.x += p.vx
    p.y += p.vy
    if (p.x < -20) p.x = w + 20
    if (p.x > w + 20) p.x = -20
    if (p.y < -20) p.y = h + 20
    if (p.y > h + 20) p.y = -20

    p._dy = drawY
    drawTuiParticle(p.x, p._dy, p.size, r, g, b)
  }

  drawConnections(w, h)

  const bgColor = getComputedStyle(document.documentElement)
    .getPropertyValue('--color-border-color')
    .trim()
  if (bgColor && bgColor.startsWith('#')) {
    const [br, bg, bb] = parseBgAccent(bgColor)
    for (const p of bgParticles) {
      const dy = (p as any)._dy ?? p.y - scrollY * p.parallax
      const dx = p.x - mouseX
      const dist2 = dx * dx + dy * dy
      if (dist2 < 6400 && dist2 > 1) {
        const dist = Math.sqrt(dist2)
        const force = 0.3 / dist
        p.vx += (dx / dist) * force
        p.vy += (dy / dist) * force
      }

      p.vx *= 0.99
      p.vy *= 0.99

      const minSpeed = 0.1
      const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy)
      if (speed < minSpeed) {
        const angle = Math.atan2((Math.random() - 0.5) * 0.15, (Math.random() - 0.5) * 0.15)
        p.vx = Math.cos(angle) * minSpeed
        p.vy = Math.sin(angle) * minSpeed
      }

      p.x += p.vx
      p.y += p.vy
      if (p.x < -10) p.x = w + 10
      if (p.x > w + 10) p.x = -10
      if (p.y < -10) p.y = h + 10
      if (p.y > h + 10) p.y = -10

      const drawY = p.y - scrollY * p.parallax
      drawBgParticle(p.x, drawY, p.size, br, bg, bb)
    }
  }

  animId = requestAnimationFrame(animate)
}

function initCanvas() {
  const c = canvas.value
  if (!c) return

  accentColor = getComputedStyle(document.documentElement)
    .getPropertyValue('--color-link')
    .trim()

  if (!accentColor || !accentColor.startsWith('#')) return

  const w = window.innerWidth
  const h = window.innerHeight
  const dpr = window.devicePixelRatio || 1

  c.width = w * dpr
  c.height = h * dpr
  c.style.width = `${w}px`
  c.style.height = `${h}px`

  if (ctx) ctx.scale(dpr, dpr)
  particles = createParticles(w, h)
  bgParticles = createBgParticles(w, h)
}

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

  initCanvas()
  ctx = canvas.value.getContext('2d')

  const observer = new MutationObserver(() => {
    accentColor = getComputedStyle(document.documentElement)
      .getPropertyValue('--color-link')
      .trim()
    if (canvas.value) {
      const w = window.innerWidth
      const h = window.innerHeight
      canvas.value.style.width = `${w}px`
      canvas.value.style.height = `${h}px`
    }
  })
  observer.observe(document.documentElement, { attributes: true })

  window.addEventListener('resize', () => {
    if (!canvas.value || !ctx) return
    const w = window.innerWidth
    const h = window.innerHeight
    const dpr = window.devicePixelRatio || 1
    canvas.value.width = w * dpr
    canvas.value.height = h * dpr
    canvas.value.style.width = `${w}px`
    canvas.value.style.height = `${h}px`
    ctx.setTransform(1, 0, 0, 1, 0, 0)
    ctx.scale(dpr, dpr)
    // particles = createParticles(w, h)
    // bgParticles = createBgParticles(w, h)
  })

  if (ctx && canvas.value) {
    animate()
  }

  window.addEventListener('scroll', () => {
    scrollY = window.scrollY
  }, { passive: true })

  window.addEventListener('mousemove', (e) => {
    mouseX = e.clientX
    mouseY = e.clientY
  }, { passive: true })
})

onBeforeUnmount(() => {
  cancelAnimationFrame(animId)
})
</script>

<template>
  <canvas ref="canvas" class="animated-bg" aria-hidden="true" />
</template>

<style lang="scss">
.animated-bg {
  position: fixed !important;
  top: 0 !important;
  right: 0 !important;
  bottom: 0 !important;
  left: 0 !important;
  z-index: -1 !important;
  pointer-events: none !important;
  width: 100vw !important;
  height: 100vh !important;
}
</style>
