import { useEffect, useRef } from 'react'

// PetalFall is the cozy ambient layer: a fixed, non-interactive 2D canvas that
// drifts soft cherry-blossom petals down the page. Each petal is a real
// petal-shaped sprite (notched sakura silhouette with a gentle gradient and
// soft alpha), pre-rendered once per color and then blitted many times with
// rotation + sway. It sits behind the chrome, ignores pointer events, pauses
// when the tab is hidden, and renders nothing for prefers-reduced-motion.

// Saturated-but-soft blossom colors. Each petal fades from a light, near-white
// tip to its color at the base, so it reads as a real petal rather than a blob.
const PALETTE: { r: number; g: number; b: number }[] = [
  { r: 255, g: 150, b: 190 }, // rose pink
  { r: 255, g: 184, b: 205 }, // pale blossom
  { r: 255, g: 158, b: 130 }, // warm peach
  { r: 209, g: 160, b: 255 }, // lavender
  { r: 255, g: 122, b: 172 }, // deep rose
]

// Night-mode sparkle palette: soft starlight tones for the small twinkle accents
// scattered between the chunky cartoon stars (moonlit white, warm gold).
const STAR_PALETTE: { r: number; g: number; b: number }[] = [
  { r: 255, g: 255, b: 255 }, // white
  { r: 255, g: 240, b: 200 }, // warm gold
]

// Chunky cartoon-star colors — bold, candy-bright fills à la a Mario power star
// or a Kirby warp star. Each is drawn with a glossy gradient + a puffy outline.
const CARTOON_COLORS: { r: number; g: number; b: number }[] = [
  { r: 255, g: 201, b: 56 }, // golden power-star
  { r: 255, g: 150, b: 190 }, // bubblegum pink
  { r: 130, g: 195, b: 255 }, // sky blue
  { r: 150, g: 225, b: 190 }, // mint
  { r: 200, g: 175, b: 255 }, // lavender
]

const SPRITE_PX = 160 // off-screen render resolution per petal sprite

function rgba(c: { r: number; g: number; b: number }, a: number): string {
  return `rgba(${Math.round(c.r)}, ${Math.round(c.g)}, ${Math.round(c.b)}, ${a})`
}
function mixWhite(c: { r: number; g: number; b: number }, t: number) {
  return { r: c.r + (255 - c.r) * t, g: c.g + (255 - c.g) * t, b: c.b + (255 - c.b) * t }
}
function scale(c: { r: number; g: number; b: number }, t: number) {
  return { r: c.r * t, g: c.g * t, b: c.b * t }
}

// Draw one petal sprite: a notched sakura silhouette filled with a tip→base
// gradient and a faint central highlight, on a transparent canvas.
function makeSprite(color: { r: number; g: number; b: number }): HTMLCanvasElement {
  const s = SPRITE_PX
  const cv = document.createElement('canvas')
  cv.width = s
  cv.height = s
  const g = cv.getContext('2d')!
  g.translate(s / 2, s / 2)

  const hw = s * 0.3 // half width
  const hh = s * 0.44 // half height

  g.beginPath()
  g.moveTo(0, hh) // pointed base
  g.bezierCurveTo(hw, hh * 0.42, hw * 0.92, -hh * 0.52, hw * 0.2, -hh * 0.92) // right side → near tip
  g.quadraticCurveTo(0, -hh * 0.72, -hw * 0.2, -hh * 0.92) // the sakura notch
  g.bezierCurveTo(-hw * 0.92, -hh * 0.52, -hw, hh * 0.42, 0, hh) // left side → base
  g.closePath()

  const grad = g.createLinearGradient(0, -hh, 0, hh)
  grad.addColorStop(0, rgba(mixWhite(color, 0.55), 0.78)) // light, soft tip
  grad.addColorStop(0.45, rgba(color, 0.88))
  grad.addColorStop(1, rgba(scale(color, 0.8), 0.95)) // deeper base
  g.fillStyle = grad
  g.fill()

  // A soft white sheen down the centre for a delicate, glossy feel.
  const sheen = g.createLinearGradient(-hw * 0.3, 0, hw * 0.3, 0)
  sheen.addColorStop(0, 'rgba(255,255,255,0)')
  sheen.addColorStop(0.5, 'rgba(255,255,255,0.28)')
  sheen.addColorStop(1, 'rgba(255,255,255,0)')
  g.fillStyle = sheen
  g.fill()

  return cv
}

// Draw one star sprite: a soft radial glow with a crisp four-point sparkle on
// top, filled brightest at the core so it reads as a twinkling star.
function makeStarSprite(color: { r: number; g: number; b: number }): HTMLCanvasElement {
  const s = SPRITE_PX
  const cv = document.createElement('canvas')
  cv.width = s
  cv.height = s
  const g = cv.getContext('2d')!
  g.translate(s / 2, s / 2)

  // Soft halo behind the sparkle.
  const glow = g.createRadialGradient(0, 0, 0, 0, 0, s * 0.5)
  glow.addColorStop(0, rgba(mixWhite(color, 0.5), 0.5))
  glow.addColorStop(0.35, rgba(color, 0.16))
  glow.addColorStop(1, rgba(color, 0))
  g.fillStyle = glow
  g.beginPath()
  g.arc(0, 0, s * 0.5, 0, Math.PI * 2)
  g.fill()

  // Four-point sparkle: alternate a long outer radius with a small inner one so
  // the points pinch in sharply.
  const R = s * 0.46
  const r = s * 0.08
  g.beginPath()
  for (let i = 0; i < 8; i++) {
    const ang = (i * Math.PI) / 4 - Math.PI / 2
    const rad = i % 2 === 0 ? R : r
    const x = Math.cos(ang) * rad
    const y = Math.sin(ang) * rad
    if (i === 0) g.moveTo(x, y)
    else g.lineTo(x, y)
  }
  g.closePath()
  const core = g.createRadialGradient(0, 0, 0, 0, 0, R)
  core.addColorStop(0, rgba(mixWhite(color, 0.7), 0.98))
  core.addColorStop(0.5, rgba(color, 0.9))
  core.addColorStop(1, rgba(color, 0.2))
  g.fillStyle = core
  g.fill()

  return cv
}

// Draw one chunky cartoon star — a fat five-point star with a glossy radial
// fill, a soft outer glow so it pops off the dark sky, a puffy rounded outline
// (round line joins → Kirby-ish soft points), and a little corner shine. Reads
// like a game power-up rather than a delicate sparkle.
function makeCartoonStar(base: { r: number; g: number; b: number }): HTMLCanvasElement {
  const s = SPRITE_PX
  const cv = document.createElement('canvas')
  cv.width = s
  cv.height = s
  const g = cv.getContext('2d')!
  g.translate(s / 2, s / 2)

  const R = s * 0.4 // outer point radius
  const r = R * 0.46 // inner radius — small ⇒ chunky, defined points

  // Soft outer glow so the star separates from the night background.
  const glow = g.createRadialGradient(0, 0, R * 0.3, 0, 0, R * 1.35)
  glow.addColorStop(0, rgba(base, 0.35))
  glow.addColorStop(1, rgba(base, 0))
  g.fillStyle = glow
  g.beginPath()
  g.arc(0, 0, R * 1.35, 0, Math.PI * 2)
  g.fill()

  // Five-point star path (start at the top point).
  const star = () => {
    g.beginPath()
    for (let i = 0; i < 10; i++) {
      const ang = -Math.PI / 2 + (i * Math.PI) / 5
      const rad = i % 2 === 0 ? R : r
      const x = Math.cos(ang) * rad
      const y = Math.sin(ang) * rad
      if (i === 0) g.moveTo(x, y)
      else g.lineTo(x, y)
    }
    g.closePath()
  }

  // Candy fill: bright near the top-left, deepening toward the lower edge.
  const fill = g.createRadialGradient(-R * 0.25, -R * 0.3, R * 0.1, 0, 0, R * 1.1)
  fill.addColorStop(0, rgba(mixWhite(base, 0.7), 1))
  fill.addColorStop(0.5, rgba(base, 1))
  fill.addColorStop(1, rgba(scale(base, 0.82), 1))
  star()
  g.fillStyle = fill
  g.fill()

  // Puffy outline in a deeper shade of the same hue (round joins soften the
  // points so it feels hand-drawn, not spiky).
  g.lineJoin = 'round'
  g.lineCap = 'round'
  g.lineWidth = s * 0.05
  g.strokeStyle = rgba(scale(base, 0.5), 0.95)
  star()
  g.stroke()

  // Glossy shine: a small soft white blob up in the top-left lobe.
  const shine = g.createRadialGradient(-R * 0.22, -R * 0.32, 0, -R * 0.22, -R * 0.32, R * 0.3)
  shine.addColorStop(0, 'rgba(255,255,255,0.85)')
  shine.addColorStop(1, 'rgba(255,255,255,0)')
  g.fillStyle = shine
  g.beginPath()
  g.ellipse(-R * 0.22, -R * 0.32, R * 0.26, R * 0.18, -0.5, 0, Math.PI * 2)
  g.fill()

  return cv
}

interface Petal {
  baseX: number
  y: number
  size: number
  vy: number // fall speed (px/s)
  angle: number
  spin: number // rad/s
  swayAmp: number
  swayFreq: number
  swayPhase: number
  alpha: number
  sprite: number
}

export function PetalFall({ night = false }: { night?: boolean }) {
  const canvasRef = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    const reduce =
      typeof window !== 'undefined' &&
      window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
    if (reduce) return // respect users who don't want ambient motion

    const el = canvasRef.current
    if (!el) return
    const c2d = el.getContext('2d')
    if (!c2d) return
    // Non-null aliases so the render closures keep the narrowed types.
    const canvas = el
    const ctx = c2d

    // Night sky = chunky cartoon stars (the stars of the show) with a couple of
    // small twinkle sparkles mixed in for depth. Random sprite pick weights it
    // ~70% chunky stars.
    const sprites = night
      ? [...CARTOON_COLORS.map(makeCartoonStar), ...STAR_PALETTE.map(makeStarSprite)]
      : PALETTE.map(makeSprite)
    const dpr = Math.min(window.devicePixelRatio || 1, 2)
    let W = window.innerWidth
    let H = window.innerHeight

    // Scale petal count to the viewport so it stays sparse and gentle.
    const count = Math.round(Math.min(46, Math.max(16, (W * H) / 46000)))
    const rnd = (a: number, b: number) => a + Math.random() * (b - a)

    // Stars are smaller, drift down a touch slower, sway less, and barely spin
    // (they twinkle in place instead of tumbling like petals).
    const spawn = (initial: boolean): Petal =>
      night
        ? {
            baseX: rnd(0, W),
            y: initial ? rnd(-H, H) : rnd(-60, -20),
            size: rnd(13, 32),
            vy: rnd(14, 36),
            angle: rnd(0, Math.PI * 2),
            // Every star visibly spins — random direction, and a guaranteed
            // minimum speed (a 5-point star is symmetric every 72°, so a slow
            // rate reads as motionless). Varied so no two turn quite alike.
            spin: (Math.random() < 0.5 ? -1 : 1) * rnd(0.5, 2.2),
            swayAmp: 0, // stars fall straight down — no petal-like drift
            swayFreq: rnd(0.4, 1.1), // still drives the twinkle shimmer below
            swayPhase: rnd(0, Math.PI * 2),
            alpha: rnd(0.55, 0.9),
            sprite: Math.floor(Math.random() * sprites.length),
          }
        : {
            baseX: rnd(0, W),
            y: initial ? rnd(-H, H) : rnd(-60, -20),
            size: rnd(16, 38),
            vy: rnd(22, 52),
            angle: rnd(0, Math.PI * 2),
            spin: rnd(-0.9, 0.9),
            swayAmp: rnd(14, 42),
            swayFreq: rnd(0.3, 0.85),
            swayPhase: rnd(0, Math.PI * 2),
            alpha: rnd(0.5, 0.82),
            sprite: Math.floor(Math.random() * sprites.length),
          }

    let petals = Array.from({ length: count }, () => spawn(true))

    function resize() {
      W = window.innerWidth
      H = window.innerHeight
      canvas.width = Math.floor(W * dpr)
      canvas.height = Math.floor(H * dpr)
      canvas.style.width = `${W}px`
      canvas.style.height = `${H}px`
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
    }
    resize()
    window.addEventListener('resize', resize)

    let raf = 0
    let running = true
    let last = performance.now()

    function frame(now: number) {
      if (!running) return
      const dt = Math.min(0.05, (now - last) / 1000) // clamp big gaps (tab wake)
      last = now
      const t = now / 1000
      ctx.clearRect(0, 0, W, H)

      for (const p of petals) {
        p.y += p.vy * dt
        p.angle += p.spin * dt
        const x = p.baseX + p.swayAmp * Math.sin(t * p.swayFreq + p.swayPhase)

        // Recycle once fully past the bottom.
        if (p.y - p.size > H) {
          Object.assign(p, spawn(false))
          continue
        }

        // Stars shimmer gently (shallow pulse so the chunky ones glow rather
        // than blink); petals hold a steady alpha.
        const alpha = night
          ? p.alpha * (0.8 + 0.2 * Math.sin(t * p.swayFreq * 2.2 + p.swayPhase))
          : p.alpha

        ctx.save()
        ctx.translate(x, p.y)
        ctx.rotate(p.angle)
        ctx.globalAlpha = alpha
        ctx.drawImage(sprites[p.sprite], -p.size / 2, -p.size / 2, p.size, p.size)
        ctx.restore()
      }
      raf = requestAnimationFrame(frame)
    }

    const onVisibility = () => {
      if (document.hidden) {
        running = false
        cancelAnimationFrame(raf)
      } else if (!running) {
        running = true
        last = performance.now()
        raf = requestAnimationFrame(frame)
      }
    }
    document.addEventListener('visibilitychange', onVisibility)
    raf = requestAnimationFrame(frame)

    return () => {
      running = false
      cancelAnimationFrame(raf)
      window.removeEventListener('resize', resize)
      document.removeEventListener('visibilitychange', onVisibility)
      petals = []
    }
  }, [night])

  return (
    <canvas
      ref={canvasRef}
      aria-hidden
      className="petal-fall pointer-events-none fixed inset-0"
      style={{ zIndex: 20 }}
    />
  )
}
