'use client'

import { useEffect, useRef } from 'react'
import { audioState } from '@/lib/audioState'
import { useBgStore } from '@/store/bgStore'

type Pt   = { x: number; y: number; vx: number; vy: number; r: number; a: number }
type Ring = { r: number; alpha: number; speed: number }
type Star = { x: number; y: number; r: number; ba: number; ph: number; sp: number }
type Drop = { x: number; y: number; speed: number; len: number; alpha: number }

export default function AudioBackground() {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const { bgMode, fxConfigs } = useBgStore()
  const fxRef = useRef(fxConfigs)

  // Sync config changes without restarting the animation loop
  useEffect(() => { fxRef.current = fxConfigs }, [fxConfigs])

  useEffect(() => {
    if (bgMode === 'none') return

    const canvas = canvasRef.current
    if (!canvas) return
    const ctx = canvas.getContext('2d')
    if (!ctx) return

    let rafId: number
    let smoothBass = 0
    let smoothMid  = 0
    let fastBass   = 0
    let dataBuf: Uint8Array<ArrayBuffer> | null = null

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

    const tick = () => {
      let rB = 0, rM = 0
      if (audioState.analyser && audioState.isPlaying) {
        const n = audioState.analyser.frequencyBinCount
        if (!dataBuf || dataBuf.length !== n) dataBuf = new Uint8Array(new ArrayBuffer(n))
        audioState.analyser.getByteFrequencyData(dataBuf)
        const bEnd = Math.max(1, Math.ceil(n * 0.1))
        for (let i = 0; i < bEnd; i++) rB = Math.max(rB, dataBuf[i] / 255)
        const mEnd = Math.ceil(n * 0.4); let mSum = 0
        for (let i = bEnd; i < mEnd; i++) mSum += dataBuf[i] / 255
        rM = mSum / Math.max(1, mEnd - bEnd)
      }
      fastBass   += (rB - fastBass)   * 0.45
      smoothBass += (rB - smoothBass) * 0.1
      smoothMid  += (rM - smoothMid)  * 0.07
    }

    const ac = () => document.documentElement.style.getPropertyValue('--accent-rgb') || '200,255,0'

    const clear = (W: number, H: number, trail: number) => {
      if (trail > 0.005) {
        ctx.fillStyle = `rgba(10,10,15,${(1 - trail).toFixed(3)})`
        ctx.fillRect(0, 0, W, H)
      } else {
        ctx.clearRect(0, 0, W, H)
        ctx.fillStyle = '#0a0a0f'
        ctx.fillRect(0, 0, W, H)
      }
    }

    // ── ORBS ─────────────────────────────────────────────────────────────────
    const drawOrbs = () => {
      const W = canvas.width, H = canvas.height, a = ac()
      const cfg = fxRef.current.orbs
      const t = Date.now() / (5000 / cfg.speed)
      const br = Math.sin(t) * 0.04 + Math.cos(t * 0.7) * 0.02
      const diag = Math.hypot(W, H), base = diag * 0.62
      const bri = cfg.brightness

      clear(W, H, cfg.trail)

      const r1 = base * (0.78 + smoothBass * 0.45 + br)
      const a1 = (0.07 + smoothBass * 0.08) * bri
      const g1 = ctx.createRadialGradient(W * 0.12, H * 0.06, 0, W * 0.12, H * 0.06, r1)
      g1.addColorStop(0, `rgba(${a},${a1})`); g1.addColorStop(0.42, `rgba(${a},${a1 * 0.25})`); g1.addColorStop(1, `rgba(${a},0)`)
      ctx.fillStyle = g1; ctx.fillRect(0, 0, W, H)

      const r2 = base * (0.70 + smoothMid * 0.40 - br * 0.5)
      const a2 = (0.06 + smoothMid * 0.07) * bri
      const g2 = ctx.createRadialGradient(W * 0.88, H * 0.94, 0, W * 0.88, H * 0.94, r2)
      g2.addColorStop(0, `rgba(255,60,172,${a2})`); g2.addColorStop(0.42, `rgba(255,60,172,${a2 * 0.25})`); g2.addColorStop(1, 'rgba(255,60,172,0)')
      ctx.fillStyle = g2; ctx.fillRect(0, 0, W, H)

      const c = smoothBass * 0.5 + smoothMid * 0.5
      if (c > 0.008) {
        const r3 = base * (0.40 + c * 0.35)
        const g3 = ctx.createRadialGradient(W * 0.5, H * 0.5, 0, W * 0.5, H * 0.5, r3)
        g3.addColorStop(0, `rgba(140,100,255,${(0.035 + c * 0.06) * bri})`); g3.addColorStop(1, 'rgba(140,100,255,0)')
        ctx.fillStyle = g3; ctx.fillRect(0, 0, W, H)
      }
    }

    // ── WAVES ─────────────────────────────────────────────────────────────────
    const drawWaves = () => {
      const W = canvas.width, H = canvas.height, a = ac()
      const cfg = fxRef.current.waves
      const t = (Date.now() / 1000) * cfg.speed
      const amp = cfg.amplitude
      clear(W, H, cfg.trail)
      const layers = [
        { y: 0.70, amp: (20 + smoothBass * 60) * amp, freq: 0.007, ph: t * 0.35,  al: 0.12, c: `rgba(${a},` },
        { y: 0.76, amp: (16 + smoothMid  * 45) * amp, freq: 0.011, ph: -t * 0.5,  al: 0.09, c: 'rgba(255,60,172,' },
        { y: 0.81, amp: (24 + smoothBass * 75) * amp, freq: 0.005, ph: t * 0.28,  al: 0.14, c: `rgba(${a},` },
        { y: 0.87, amp: (12 + smoothMid  * 35) * amp, freq: 0.013, ph: -t * 0.62, al: 0.08, c: 'rgba(140,100,255,' },
        { y: 0.93, amp: (30 + smoothBass * 90) * amp, freq: 0.004, ph: t * 0.18,  al: 0.18, c: `rgba(${a},` },
      ]
      for (const l of layers) {
        const baseY = H * l.y
        ctx.beginPath(); ctx.moveTo(-2, baseY)
        for (let x = 0; x <= W + 3; x += 3)
          ctx.lineTo(x, baseY + Math.sin(x * l.freq + l.ph) * l.amp + Math.sin(x * l.freq * 2.4 + l.ph * 1.6) * l.amp * 0.25)
        ctx.lineTo(W + 2, H + 2); ctx.lineTo(-2, H + 2); ctx.closePath()
        const g = ctx.createLinearGradient(0, baseY - l.amp * 1.5, 0, H)
        g.addColorStop(0, `${l.c}${l.al})`); g.addColorStop(1, `${l.c}0)`)
        ctx.fillStyle = g; ctx.fill()
      }
    }

    // ── PARTICLES ─────────────────────────────────────────────────────────────
    const PTS: Pt[] = Array.from({ length: 55 }, () => ({
      x: Math.random(), y: Math.random(),
      vx: (Math.random() - 0.5) * 0.00025, vy: (Math.random() - 0.5) * 0.00025,
      r: Math.random() * 1.5 + 0.6, a: Math.random() * 0.35 + 0.1,
    }))
    const drawParticles = () => {
      const W = canvas.width, H = canvas.height, a = ac()
      const cfg = fxRef.current.particles
      clear(W, H, cfg.trail)
      const spd = (1 + smoothBass * 3) * cfg.speed
      for (const p of PTS) {
        p.x += p.vx * spd; p.y += p.vy * spd
        if (p.x < 0) p.x += 1; if (p.x > 1) p.x -= 1
        if (p.y < 0) p.y += 1; if (p.y > 1) p.y -= 1
        if (smoothBass > 0.35 && Math.random() < 0.07) {
          p.vx += (Math.random() - 0.5) * 0.0006; p.vy += (Math.random() - 0.5) * 0.0006
          const mv = 0.0012
          p.vx = Math.max(-mv, Math.min(mv, p.vx)); p.vy = Math.max(-mv, Math.min(mv, p.vy))
        }
        ctx.beginPath(); ctx.arc(p.x * W, p.y * H, p.r * (1 + smoothBass * 1.2), 0, Math.PI * 2)
        ctx.fillStyle = `rgba(${a},${p.a * (0.7 + smoothMid * 0.3)})`; ctx.fill()
      }
      const maxD = Math.min(W, H) * 0.12 * cfg.linkDist; ctx.lineWidth = 0.5
      for (let i = 0; i < PTS.length; i++)
        for (let j = i + 1; j < PTS.length; j++) {
          const dx = (PTS[i].x - PTS[j].x) * W, dy = (PTS[i].y - PTS[j].y) * H
          const d = Math.sqrt(dx * dx + dy * dy)
          if (d < maxD) {
            ctx.beginPath(); ctx.moveTo(PTS[i].x * W, PTS[i].y * H); ctx.lineTo(PTS[j].x * W, PTS[j].y * H)
            ctx.strokeStyle = `rgba(${a},${(1 - d / maxD) * 0.07 * (1 + smoothMid)})`; ctx.stroke()
          }
        }
    }

    // ── AURORA ────────────────────────────────────────────────────────────────
    const drawAurora = () => {
      const W = canvas.width, H = canvas.height, a = ac()
      const cfg = fxRef.current.aurora
      const t = (Date.now() / 3500) * cfg.speed
      const bri = cfg.brightness
      clear(W, H, cfg.trail)

      const bands = [
        { cx: 0.10, cy: 0.16, w: 0.72, h: 0.22, ph: t * 0.7,  al: (0.10 + smoothBass * 0.08) * bri, c: a },
        { cx: 0.58, cy: 0.09, w: 0.88, h: 0.18, ph: -t * 0.5, al: (0.08 + smoothMid  * 0.07) * bri, c: '140,100,255' },
        { cx: 0.85, cy: 0.30, w: 0.62, h: 0.20, ph: t * 0.6,  al: (0.09 + smoothBass * 0.07) * bri, c: '255,60,172' },
        { cx: 0.30, cy: 0.50, w: 0.78, h: 0.16, ph: -t * 0.8, al: (0.07 + smoothMid  * 0.06) * bri, c: a },
        { cx: 0.70, cy: 0.64, w: 0.58, h: 0.18, ph: t * 0.5,  al: (0.06 + smoothBass * 0.05) * bri, c: '140,100,255' },
        { cx: 0.18, cy: 0.78, w: 0.68, h: 0.16, ph: -t * 0.65,al: (0.05 + smoothMid  * 0.05) * bri, c: '255,60,172' },
      ]
      for (const band of bands) {
        const cx = band.cx * W, cy = (band.cy + Math.sin(band.ph) * 0.07) * H
        const rw = band.w * W,  rh = band.h * H * (1 + smoothBass * 0.3)
        ctx.save(); ctx.translate(cx, cy); ctx.scale(1, rh / rw)
        const g = ctx.createRadialGradient(0, 0, 0, 0, 0, rw)
        g.addColorStop(0, `rgba(${band.c},${band.al})`); g.addColorStop(0.45, `rgba(${band.c},${band.al * 0.4})`); g.addColorStop(1, `rgba(${band.c},0)`)
        ctx.fillStyle = g; ctx.beginPath(); ctx.arc(0, 0, rw, 0, Math.PI * 2); ctx.fill(); ctx.restore()
      }
    }

    // ── PULSE — expanding ring waves ──────────────────────────────────────────
    const RINGS: Ring[] = []
    let lastRingMs   = 0
    let lastAmbMs    = 0

    const drawPulse = () => {
      const W = canvas.width, H = canvas.height, a = ac()
      const cfg = fxRef.current.pulse
      const cx = W * 0.5, cy = H * 0.5
      const maxR = Math.hypot(W, H) * 0.72

      clear(W, H, cfg.trail)

      const now = Date.now()

      // Beat-triggered ring
      const threshold = 0.45 - cfg.sensitivity * 0.3
      if (fastBass > threshold && fastBass > smoothBass + 0.05 && now - lastRingMs > 200) {
        RINGS.push({ r: 4, alpha: 1.0, speed: (5 + fastBass * 12) * cfg.ringSpeed })
        lastRingMs = now
      }

      // Ambient ring on timer so it always looks alive even without audio
      if (now - lastAmbMs > 1800) {
        RINGS.push({ r: 4, alpha: 0.7, speed: 5 * cfg.ringSpeed })
        lastAmbMs = now
      }

      for (let i = RINGS.length - 1; i >= 0; i--) {
        const ring = RINGS[i]
        ring.r     += ring.speed
        ring.alpha *= 0.982    // exponential fade — stays bright longer, fades smoothly
        if (ring.alpha < 0.015 || ring.r > maxR) { RINGS.splice(i, 1); continue }

        const lw = 2 + ring.alpha * 5

        // Outer glow
        ctx.beginPath(); ctx.arc(cx, cy, ring.r, 0, Math.PI * 2)
        ctx.strokeStyle = `rgba(${a},${ring.alpha * 0.2})`
        ctx.lineWidth = lw * 5; ctx.stroke()

        // Main ring
        ctx.beginPath(); ctx.arc(cx, cy, ring.r, 0, Math.PI * 2)
        ctx.strokeStyle = `rgba(${a},${ring.alpha})`
        ctx.lineWidth = lw; ctx.stroke()

        // Pink echo
        if (ring.r > 30 && ring.alpha > 0.08) {
          ctx.beginPath(); ctx.arc(cx, cy, ring.r * 0.85, 0, Math.PI * 2)
          ctx.strokeStyle = `rgba(255,60,172,${ring.alpha * 0.35})`
          ctx.lineWidth = lw * 0.55; ctx.stroke()
        }
      }

      // Center dot — pulses with bass, small footprint so it doesn't drown rings
      const dotR = 14 + smoothBass * 55
      const cg = ctx.createRadialGradient(cx, cy, 0, cx, cy, dotR)
      cg.addColorStop(0,   `rgba(${a},${0.85 + smoothBass * 0.15})`)
      cg.addColorStop(0.45,`rgba(${a},0.25)`)
      cg.addColorStop(1,   `rgba(${a},0)`)
      ctx.fillStyle = cg
      ctx.beginPath(); ctx.arc(cx, cy, dotR, 0, Math.PI * 2); ctx.fill()
    }

    // ── STARS ─────────────────────────────────────────────────────────────────
    const STARS: Star[] = Array.from({ length: 180 }, () => ({
      x: Math.random(), y: Math.random(), r: Math.random() * 1.4 + 0.3,
      ba: Math.random() * 0.55 + 0.15, ph: Math.random() * Math.PI * 2, sp: Math.random() * 1.5 + 0.4,
    }))
    const drawStars = () => {
      const W = canvas.width, H = canvas.height, a = ac()
      const cfg = fxRef.current.stars
      const t = Date.now() / 1000
      const bri = cfg.brightness
      clear(W, H, cfg.trail)
      const n1 = ctx.createRadialGradient(W*0.30, H*0.35, 0, W*0.30, H*0.35, W*0.55)
      n1.addColorStop(0, `rgba(${a},${(0.04+smoothMid*0.04)*bri})`); n1.addColorStop(1, `rgba(${a},0)`)
      ctx.fillStyle = n1; ctx.fillRect(0, 0, W, H)
      for (const s of STARS) {
        const tw = (Math.sin(t * s.sp * cfg.twinkle + s.ph) + 1) * 0.5
        const alpha = s.ba * (0.35 + tw * 0.65) * (1 + smoothMid * 0.4) * bri
        const r = s.r * (1 + smoothBass * tw * 1.0)
        if (s.r > 1.1) { ctx.beginPath(); ctx.arc(s.x*W, s.y*H, r*2.8, 0, Math.PI*2); ctx.fillStyle = `rgba(${a},${alpha*0.18})`; ctx.fill() }
        ctx.beginPath(); ctx.arc(s.x*W, s.y*H, r, 0, Math.PI*2); ctx.fillStyle = `rgba(${a},${alpha})`; ctx.fill()
      }
    }

    // ── RAIN ──────────────────────────────────────────────────────────────────
    const DROPS: Drop[] = Array.from({ length: 60 }, () => ({
      x: Math.random(), y: Math.random(),
      speed: Math.random() * 0.0015 + 0.0008,
      len: Math.random() * 0.055 + 0.03,
      alpha: Math.random() * 0.35 + 0.12,
    }))
    const drawRain = () => {
      const W = canvas.width, H = canvas.height, a = ac()
      const cfg = fxRef.current.rain
      const count = Math.round(cfg.drops)
      clear(W, H, cfg.trail)
      const spd = (1 + smoothBass * 2.5) * cfg.speed
      for (let i = 0; i < count && i < DROPS.length; i++) {
        const d = DROPS[i]
        d.y += d.speed * spd
        if (d.y > 1.08) { d.y = -d.len - 0.02; d.x = Math.random() }
        const x = d.x * W, y = d.y * H, len = d.len * H * (1 + smoothBass * 0.4)
        const g = ctx.createLinearGradient(x, y - len, x, y)
        g.addColorStop(0, `rgba(${a},0)`); g.addColorStop(0.5, `rgba(${a},${d.alpha * 0.3})`); g.addColorStop(1, `rgba(${a},${d.alpha})`)
        ctx.fillStyle = g; ctx.fillRect(x - 0.7, y - len, 1.4, len)
        ctx.beginPath(); ctx.arc(x, y, 1.4, 0, Math.PI * 2); ctx.fillStyle = `rgba(${a},${d.alpha})`; ctx.fill()
      }
    }

    // ── RAYS ──────────────────────────────────────────────────────────────────
    const drawRays = () => {
      const W = canvas.width, H = canvas.height, a = ac()
      const cfg = fxRef.current.rays
      const t = (Date.now() / 1000) * cfg.speed * 0.08
      const cx = W * 0.5, cy = H * 0.55
      const maxR = Math.hypot(W, H) * 1.1
      const br = cfg.brightness
      const sp = cfg.spread

      clear(W, H, cfg.trail)

      const count = cfg.count
      for (let i = 0; i < count; i++) {
        const angle = (i / count) * Math.PI * 2 + t
        const isMain = i % 2 === 0
        const hw = Math.tan((0.055 + smoothBass * 0.035) * sp) * maxR
        const al = ((isMain ? 0.12 : 0.07) + smoothBass * 0.10 + smoothMid * 0.03) * br

        const ex = cx + Math.cos(angle) * maxR, ey = cy + Math.sin(angle) * maxR
        const px = -Math.sin(angle) * hw, py =  Math.cos(angle) * hw

        ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(ex + px, ey + py); ctx.lineTo(ex - px, ey - py); ctx.closePath()
        const grad = ctx.createLinearGradient(cx, cy, ex, ey)
        grad.addColorStop(0,    `rgba(${a},${al * 2.5})`)
        grad.addColorStop(0.12, `rgba(${a},${al})`)
        grad.addColorStop(0.55, `rgba(${a},${al * 0.35})`)
        grad.addColorStop(1,    `rgba(${a},0)`)
        ctx.fillStyle = grad; ctx.fill()
      }

      const cnt2 = Math.max(2, Math.floor(count / 2))
      for (let i = 0; i < cnt2; i++) {
        const angle = (i / cnt2) * Math.PI * 2 - t * 0.65 + Math.PI / count
        const hw = Math.tan(0.035 * sp) * maxR
        const al = (0.05 + smoothMid * 0.06) * br

        const ex = cx + Math.cos(angle) * maxR, ey = cy + Math.sin(angle) * maxR
        const px = -Math.sin(angle) * hw, py =  Math.cos(angle) * hw

        ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(ex + px, ey + py); ctx.lineTo(ex - px, ey - py); ctx.closePath()
        const grad = ctx.createLinearGradient(cx, cy, ex, ey)
        grad.addColorStop(0,    `rgba(255,60,172,${al * 2.5})`)
        grad.addColorStop(0.15, `rgba(255,60,172,${al})`)
        grad.addColorStop(1,    'rgba(255,60,172,0)')
        ctx.fillStyle = grad; ctx.fill()
      }

      const cg = ctx.createRadialGradient(cx, cy, 0, cx, cy, 100 + smoothBass * 140)
      cg.addColorStop(0, `rgba(${a},${(0.14 + smoothBass * 0.18) * br})`); cg.addColorStop(1, `rgba(${a},0)`)
      ctx.fillStyle = cg; ctx.fillRect(0, 0, W, H)
    }

    // ── LOOP ──────────────────────────────────────────────────────────────────
    const drawFn = ({
      orbs: drawOrbs, waves: drawWaves, particles: drawParticles,
      aurora: drawAurora, pulse: drawPulse, stars: drawStars,
      rain: drawRain, rays: drawRays,
    } as Record<string, () => void>)[bgMode] ?? drawOrbs

    ctx.clearRect(0, 0, canvas.width, canvas.height)
    const loop = () => { tick(); drawFn(); rafId = requestAnimationFrame(loop) }
    loop()

    return () => { cancelAnimationFrame(rafId); window.removeEventListener('resize', resize) }
  }, [bgMode])

  if (bgMode === 'none') return null

  return (
    <canvas
      ref={canvasRef}
      className="fixed inset-0 w-full h-full pointer-events-none"
      style={{ zIndex: 0 }}
    />
  )
}
