package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.saveable.SaverScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.pixelized.desktop.lwa.ui.theme.color.LwaColors
import com.pixelized.desktop.lwa.ui.theme.lwa
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
import org.jetbrains.compose.resources.painterResource
import androidx.compose.runtime.saveable.Saver as ComposeSaver

@Stable
data class CharacterRibbonRollUio(
    val rollId: String,
    val hideDelay: Int,
    val characterSheetId: String,
    val value: Int?,
    val critical: Critical?,
) {
    @Stable
    enum class Critical {
        CRITICAL_SUCCESS,
        SPECIAL_SUCCESS,
        SUCCESS,
        FAILURE,
        CRITICAL_FAILURE
    }
}

@Stable
class CharacterRibbonRollAnimation(
    val animatedAlpha: Animatable<Float, AnimationVector1D>,
    val animatedRotation: Animatable<Float, AnimationVector1D>,
    val animatedScale: Animatable<Float, AnimationVector1D>,
) {
    constructor(
        alpha: Float = 1f,
        rotation: Float = 0f,
        scale: Float = 1f,
    ) : this(
        animatedAlpha = Animatable(alpha),
        animatedRotation = Animatable(rotation),
        animatedScale = Animatable(scale),
    )

    object Saver : ComposeSaver<CharacterRibbonRollAnimation, List<Float>> {
        override fun restore(value: List<Float>): CharacterRibbonRollAnimation {
            return CharacterRibbonRollAnimation(
                animatedAlpha = Animatable(value[0]),
                animatedRotation = Animatable(value[1]),
                animatedScale = Animatable(value[2]),
            )
        }

        override fun SaverScope.save(value: CharacterRibbonRollAnimation): List<Float> {
            return listOf(
                value.animatedAlpha.value,
                value.animatedRotation.value,
                value.animatedScale.value,
            )
        }
    }
}

@Composable
fun CharacterRibbonRoll(
    modifier: Modifier = Modifier,
    size: DpSize = MaterialTheme.lwa.dimen.portrait.minimized,
    roll: Flow<CharacterRibbonRollUio?>,
) {
    val rollValue = roll.collectAsStateWithLifecycle(initialValue = null)

    CharacterRibbonRoll(
        modifier = modifier,
        size = size,
        roll = rollValue,
    )
}

@Composable
fun CharacterRibbonRoll(
    modifier: Modifier = Modifier,
    size: DpSize = MaterialTheme.lwa.dimen.portrait.minimized,
    portrait: LwaColors.Portrait = MaterialTheme.lwa.colorScheme.portrait,
    roll: State<CharacterRibbonRollUio?>,
) {
    AnimatedContent(
        modifier = modifier
            .width(width = size.width)
            .aspectRatio(ratio = 1f)
            .graphicsLayer { clip = false },
        targetState = roll.value,
        transitionSpec = {
            val enter = fadeIn()
            val exit = fadeOut()
            enter togetherWith exit using SizeTransform(clip = false)
        }
    ) {
        val animation = diceIconAnimation(
            key = it?.rollId,
            rollDelay = it?.hideDelay ?: 1000,
        )
        val color = animateColorAsState(
            targetValue = when (it?.critical) {
                CharacterRibbonRollUio.Critical.CRITICAL_SUCCESS -> portrait.criticalSuccess
                CharacterRibbonRollUio.Critical.SPECIAL_SUCCESS -> portrait.spacialSuccess
                CharacterRibbonRollUio.Critical.SUCCESS -> portrait.success
                CharacterRibbonRollUio.Critical.FAILURE -> portrait.failure
                CharacterRibbonRollUio.Critical.CRITICAL_FAILURE -> portrait.criticalFailure
                null -> portrait.default
            }
        )
        Box(
            modifier = Modifier.graphicsLayer {
                this.alpha = animation.animatedAlpha.value
                this.scaleX = animation.animatedScale.value
                this.scaleY = animation.animatedScale.value
            },
            contentAlignment = Alignment.Center,
        ) {
            if (it != null) {
                Icon(
                    modifier = Modifier
                        .graphicsLayer {
                            this.rotationZ = animation.animatedRotation.value
                        }
                        .fillMaxWidth()
                        .aspectRatio(1f)
                        .padding(all = 16.dp),
                    painter = painterResource(Res.drawable.ic_d20_24dp),
                    tint = color.value,
                    contentDescription = null,
                )
                Text(
                    style = MaterialTheme.lwa.typography.portrait.dice,
                    textAlign = TextAlign.Center,
                    color = MaterialTheme.colors.onSurface,
                    text = it.value.toString()
                )
            }
        }
    }
}

@Composable
private fun diceIconAnimation(
    key: String?,
    rollDelay: Int,
): CharacterRibbonRollAnimation {
    val animation = rememberSaveable(
        key = key,
        saver = CharacterRibbonRollAnimation.Saver,
    ) {
        CharacterRibbonRollAnimation()
    }
    LaunchedEffect(key) {
        launch {
            animation.animatedScale.animateTo(
                targetValue = 1.20f,
                animationSpec = spring(
                    dampingRatio = Spring.DampingRatioNoBouncy,
                    stiffness = 800f,
                )
            )
            animation.animatedScale.animateTo(
                targetValue = 1f,
                animationSpec = spring(
                    dampingRatio = 0.28f,
                    stiffness = 800f,
                )
            )
        }
        launch {
            animation.animatedRotation.animateTo(
                targetValue = 360f * 3,
                animationSpec = spring(
                    dampingRatio = Spring.DampingRatioNoBouncy,
                    stiffness = Spring.StiffnessLow,
                )
            )
        }
        launch {
            animation.animatedAlpha.animateTo(
                targetValue = 0f,
                animationSpec = tween(
                    delayMillis = rollDelay,
                    durationMillis = 2000,
                    easing = FastOutSlowInEasing,
                )
            )
        }
    }
    return animation
}