<script setup lang="ts">
/**
 * C 端氛围层：暗色星空 + 偶发流星；浅色金色光斑漂移。
 * 粒子数约为 PC StarBackground 的 1/3，挂在 AppPageShell 底层。
 * 使用旧版 canvas 上下文以保证各端兼容；不可点击（pointer-events: none）。
 */
import { getCurrentInstance, nextTick, onBeforeUnmount, onMounted, watch } from 'vue'
import { onHide, onShow } from '@dcloudio/uni-app'
import { useTheme } from '@/composables/useTheme'

const { isDark } = useTheme()
const canvasId = `ambient-${Math.random().toString(36).slice(2, 9)}`

interface Star {
  x: number
  y: number
  r: number
  a: number
  out: boolean
  sp: number
}
interface Meteor {
  x: number
  y: number
  len: number
  sp: number
  a: number
}
interface Orb {
  bx: number
  by: number
  r: number
  ampX: number
  ampY: number
  phX: number
  phY: number
  sp: number
  color: string
}
interface Dust {
  x: number
  y: number
  r: number
  a: number
  out: boolean
  sp: number
  base: number
}

let ctx: UniApp.CanvasContext | null = null
let width = 0
let height = 0
let running = false
let timer: ReturnType<typeof setTimeout> | null = null
let time = 0
const stars: Star[] = []
const meteors: Meteor[] = []
const orbs: Orb[] = []
const dust: Dust[] = []

const clearPools = () => {
  stars.length = 0
  meteors.length = 0
  orbs.length = 0
  dust.length = 0
}

const initDark = () => {
  clearPools()
  for (let i = 0; i < 55; i++) {
    stars.push({
      x: Math.random() * width,
      y: Math.random() * height,
      r: Math.random() * 1.4 + 0.3,
      a: Math.random(),
      out: Math.random() > 0.5,
      sp: Math.random() * 0.02 + 0.005,
    })
  }
}

const initLight = () => {
  clearPools()
  const palette = [
    'rgba(212,179,131,0.18)',
    'rgba(196,150,90,0.14)',
    'rgba(232,200,150,0.16)',
  ]
  const places = [
    { bx: 0.18, by: 0.22 },
    { bx: 0.8, by: 0.38 },
    { bx: 0.42, by: 0.78 },
  ]
  places.forEach((p, i) => {
    orbs.push({
      bx: p.bx * width,
      by: p.by * height,
      r: Math.min(width, height) * (0.22 + Math.random() * 0.1),
      ampX: 28 + Math.random() * 40,
      ampY: 22 + Math.random() * 36,
      phX: Math.random() * Math.PI * 2,
      phY: Math.random() * Math.PI * 2,
      sp: 0.0003 + Math.random() * 0.0002,
      color: palette[i % palette.length],
    })
  })
  for (let i = 0; i < 28; i++) {
    const base = Math.random() * 0.3 + 0.12
    dust.push({
      x: Math.random() * width,
      y: Math.random() * height,
      r: Math.random() * 1.6 + 0.4,
      a: base,
      out: Math.random() > 0.5,
      sp: Math.random() * 0.008 + 0.003,
      base,
    })
  }
}

const initTheme = () => {
  if (isDark.value) initDark()
  else initLight()
}

const draw = () => {
  if (!ctx || !running) return
  time += 1
  ctx.clearRect(0, 0, width, height)

  if (isDark.value) {
    stars.forEach((s) => {
      if (s.out) {
        s.a -= s.sp
        if (s.a <= 0.1) s.out = false
      }
      else {
        s.a += s.sp
        if (s.a >= 1) s.out = true
      }
      ctx!.beginPath()
      ctx!.arc(s.x, s.y, s.r, 0, Math.PI * 2)
      ctx!.setFillStyle(`rgba(255,255,255,${Math.max(0.08, s.a)})`)
      ctx!.fill()
    })
    if (Math.random() < 0.008) {
      meteors.push({
        x: Math.random() * width,
        y: 0,
        len: Math.random() * 60 + 24,
        sp: Math.random() * 8 + 5,
        a: 1,
      })
    }
    for (let i = meteors.length - 1; i >= 0; i--) {
      const m = meteors[i]
      const ex = m.x - m.len * 0.7
      const ey = m.y - m.len * 0.7
      ctx.beginPath()
      ctx.moveTo(m.x, m.y)
      ctx.lineTo(ex, ey)
      ctx.setStrokeStyle(`rgba(255,255,255,${m.a})`)
      ctx.setLineWidth(1.2)
      ctx.stroke()
      m.x += m.sp * 0.7
      m.y += m.sp * 0.7
      m.a -= 0.012
      if (m.a <= 0 || m.y > height) meteors.splice(i, 1)
    }
  }
  else {
    orbs.forEach((o) => {
      const x = o.bx + Math.sin(time * o.sp + o.phX) * o.ampX
      const y = o.by + Math.cos(time * o.sp + o.phY) * o.ampY
      const g = ctx!.createCircularGradient(x, y, o.r)
      g.addColorStop(0, o.color)
      g.addColorStop(1, 'rgba(212,179,131,0)')
      ctx!.beginPath()
      ctx!.arc(x, y, o.r, 0, Math.PI * 2)
      ctx!.setFillStyle(g)
      ctx!.fill()
    })
    dust.forEach((d) => {
      if (d.out) {
        d.a -= d.sp
        if (d.a <= 0.05) d.out = false
      }
      else {
        d.a += d.sp
        if (d.a >= d.base) d.out = true
      }
      ctx!.beginPath()
      ctx!.arc(d.x, d.y, d.r, 0, Math.PI * 2)
      ctx!.setFillStyle(`rgba(196,150,90,${d.a})`)
      ctx!.fill()
    })
  }

  ctx.draw()
  timer = setTimeout(() => {
    if (running) draw()
  }, 48)
}

const stop = () => {
  running = false
  if (timer) {
    clearTimeout(timer)
    timer = null
  }
}

const start = () => {
  stop()
  running = true
  draw()
}

const setup = () => {
  const inst = getCurrentInstance()
  uni.getSystemInfo({
    success: (info) => {
      width = info.windowWidth
      height = info.windowHeight
      nextTick(() => {
        ctx = uni.createCanvasContext(canvasId, inst?.proxy as never)
        initTheme()
        start()
      })
    },
  })
}

onMounted(setup)
onShow(() => {
  if (ctx) start()
})
onHide(stop)
onBeforeUnmount(stop)

watch(isDark, () => {
  initTheme()
})
</script>

<template>
  <view class="ambient" aria-hidden="true">
    <canvas
      :canvas-id="canvasId"
      class="ambient-canvas"
      :style="{ width: '100%', height: '100%' }"
      disable-scroll
    />
    <view class="noise" :class="isDark ? 'noise--dark' : 'noise--light'" />
  </view>
</template>

<style scoped lang="scss">
.ambient {
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.ambient-canvas {
  width: 100%;
  height: 100%;
  display: block;
}
.noise {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* 轻量噪点：用半透明叠层近似胶片感，避免额外图片体积 */
  background-image: radial-gradient(rgba(127, 127, 127, 0.35) 0.5px, transparent 0.5px);
  background-size: 4px 4px;
}
.noise--light { opacity: 0.045; mix-blend-mode: multiply; }
.noise--dark { opacity: 0.06; mix-blend-mode: soft-light; }
</style>
