package app.closer.ui.components

import android.provider.Settings
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import app.closer.R
import kotlin.math.sin
import kotlin.random.Random

/**
 * A calm, on-brand celebration: a gentle upward drift of the designed pastel heart/petal
 * sprites with a soft haptic. Deliberately restrained (the 2026 "calm delight" direction) —
 * no screen-blocking confetti cannon.
 *
 * Drop it as the top layer of a Box. It plays once each time [visible] flips to true and calls
 * [onFinished] when the drift completes. Honors the system "remove animations" setting.
 *
 * @param intensity scales the particle count (e.g. pass the match ratio so a 5/5 game feels
 *   bigger than a 1/5). 1f ≈ the default count.
 */
@Composable
fun CelebrationOverlay(
    visible: Boolean,
    modifier: Modifier = Modifier,
    intensity: Float = 1f,
    onFinished: () -> Unit = {}
) {
    val context = LocalContext.current
    val haptics = LocalHapticFeedback.current
    val reducedMotion = remember {
        Settings.Global.getFloat(
            context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f
        ) == 0f
    }

    val progress = remember { Animatable(0f) }
    var particles by remember { mutableStateOf<List<Particle>>(emptyList()) }

    LaunchedEffect(visible) {
        if (!visible) return@LaunchedEffect
        if (reducedMotion) {
            onFinished()
            return@LaunchedEffect
        }
        haptics.performHapticFeedback(HapticFeedbackType.LongPress)
        val count = (DEFAULT_COUNT * intensity).toInt().coerceIn(MIN_COUNT, MAX_COUNT)
        particles = generateParticles(count)
        progress.snapTo(0f)
        progress.animateTo(1f, tween(DURATION_MS, easing = LinearEasing))
        onFinished()
    }

    if (!visible || reducedMotion || particles.isEmpty()) return

    val heart = ImageBitmap.imageResource(R.drawable.particle_heart)
    val petal = ImageBitmap.imageResource(R.drawable.particle_petal)

    Canvas(modifier = modifier.fillMaxSize()) {
        val t = progress.value
        particles.forEach { p -> drawParticle(p, t, heart, petal) }
    }
}

private data class Particle(
    val startXFraction: Float,
    val swayAmplitude: Float,
    val swayFrequency: Float,
    val sizeDp: Float,
    val delay: Float,
    val rotation: Float,
    val isHeart: Boolean
)

private fun generateParticles(count: Int): List<Particle> {
    val rng = Random(System.nanoTime())
    return List(count) {
        Particle(
            startXFraction = rng.nextFloat(),
            swayAmplitude = 12f + rng.nextFloat() * 28f,
            swayFrequency = 1.5f + rng.nextFloat() * 2.5f,
            sizeDp = 16f + rng.nextFloat() * 18f,
            delay = rng.nextFloat() * 0.35f,
            rotation = rng.nextFloat() * 40f - 20f,
            isHeart = rng.nextFloat() < 0.6f
        )
    }
}

private fun DrawScope.drawParticle(
    p: Particle,
    globalProgress: Float,
    heart: ImageBitmap,
    petal: ImageBitmap
) {
    // Each particle runs from its [delay] to the end of the timeline.
    val local = ((globalProgress - p.delay) / (1f - p.delay)).coerceIn(0f, 1f)
    if (local <= 0f) return

    // Rise from near the bottom (88%) to the upper third (18%).
    val y = size.height * (0.88f - 0.70f * local)
    val baseX = size.width * p.startXFraction
    val x = baseX + p.swayAmplitude * sin(local * p.swayFrequency * Math.PI).toFloat()

    // Ease in quickly, hold, then fade out over the last 35%.
    val alpha = when {
        local < 0.12f -> local / 0.12f
        local > 0.65f -> ((1f - local) / 0.35f).coerceIn(0f, 1f)
        else -> 1f
    }.coerceIn(0f, 1f)

    val sizePx = (p.sizeDp * density).toInt().coerceAtLeast(1)
    val img = if (p.isHeart) heart else petal
    rotate(degrees = p.rotation + local * 30f, pivot = Offset(x, y)) {
        drawImage(
            image = img,
            srcOffset = IntOffset.Zero,
            srcSize = IntSize(img.width, img.height),
            dstOffset = IntOffset((x - sizePx / 2f).toInt(), (y - sizePx / 2f).toInt()),
            dstSize = IntSize(sizePx, sizePx),
            alpha = alpha
        )
    }
}

private const val DEFAULT_COUNT = 18
private const val MIN_COUNT = 8
private const val MAX_COUNT = 36
private const val DURATION_MS = 1900
