package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.dice

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.PointerMatcher
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.onClick
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.input.pointer.PointerButton
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.theme.color.component.LwaButtonColors
import com.pixelized.desktop.lwa.ui.theme.lwa
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.icon_d10
import lwacharactersheet.composeapp.generated.resources.icon_d100
import lwacharactersheet.composeapp.generated.resources.icon_d12
import lwacharactersheet.composeapp.generated.resources.icon_d20
import lwacharactersheet.composeapp.generated.resources.icon_d4
import lwacharactersheet.composeapp.generated.resources.icon_d6
import lwacharactersheet.composeapp.generated.resources.icon_d8
import org.jetbrains.compose.resources.painterResource
import java.util.UUID
import kotlin.math.max
import kotlin.random.Random
import kotlin.random.nextInt

@Stable
data class DiceMenuUio(
    val d4: Int = 0,
    val d6: Int = 0,
    val d8: Int = 0,
    val d10: Int = 0,
    val d12: Int = 0,
    val d20: Int = 0,
    val d100: Int = 1,
    val result: Result? = null,
) {
    @Stable
    data class Result(
        val id: String,
        val value: Int,
    )
}

@Stable
data object DiceMenuDefault {
    @Stable
    val size: Dp = 32.dp

    @Stable
    val spacings: DpSize = DpSize(width = 2.dp, height = 8.dp)

    @Stable
    val paddings = PaddingValues(horizontal = 8.dp)
}

@Composable
fun DiceMenu(
    modifier: Modifier = Modifier,
) {
    val dices = remember {
        mutableStateOf(
            DiceMenuUio()
        )
    }
    DiceMenuContent(
        modifier = modifier,
        dices = dices,
        onAddD4 = {
            dices.value = dices.value.copy(d4 = max(0, dices.value.d4 + 1))
        },
        onRemoveD4 = {
            dices.value = dices.value.copy(d4 = max(0, dices.value.d4 - 1))
        },
        onAddD6 = {
            dices.value = dices.value.copy(d6 = max(0, dices.value.d6 + 1))
        },
        onRemoveD6 = {
            dices.value = dices.value.copy(d6 = max(0, dices.value.d6 - 1))
        },
        onAddD8 = {
            dices.value = dices.value.copy(d8 = max(0, dices.value.d8 + 1))
        },
        onRemoveD8 = {
            dices.value = dices.value.copy(d8 = max(0, dices.value.d8 - 1))
        },
        onAddD10 = {
            dices.value = dices.value.copy(d10 = max(0, dices.value.d10 + 1))
        },
        onRemoveD10 = {
            dices.value = dices.value.copy(d10 = max(0, dices.value.d10 - 1))
        },
        onAddD12 = {
            dices.value = dices.value.copy(d12 = max(0, dices.value.d12 + 1))
        },
        onRemoveD12 = {
            dices.value = dices.value.copy(d12 = max(0, dices.value.d12 - 1))
        },
        onAddD20 = {
            dices.value = dices.value.copy(d20 = max(0, dices.value.d20 + 1))
        },
        onRemoveD20 = {
            dices.value = dices.value.copy(d20 = max(0, dices.value.d20 - 1))
        },
        onAddD100 = {
            dices.value = dices.value.copy(d100 = max(0, dices.value.d100 + 1))
        },
        onRemoveD100 = {
            dices.value = dices.value.copy(d100 = max(0, dices.value.d100 - 1))
        },
        onRoll = {
            dices.value.let { dice ->
                val d4 = (0 until dice.d4)
                    .also { print("${it.last + 1}d4:(") }
                    .sumOf { Random.nextInt(range = 1..4).also { print("$it,") } }
                    .also { print(")+") }
                val d6 = (0 until dice.d6)
                    .also { print("${it.last + 1}:d6(") }
                    .sumOf { Random.nextInt(range = 1..6).also { print("$it,") } }
                    .also { print(")+") }
                val d8 = (0 until dice.d8)
                    .also { print("${it.last + 1}d8:(") }
                    .sumOf { Random.nextInt(range = 1..8).also { print("$it,") } }
                    .also { print(")+") }
                val d10 = (0 until dice.d10)
                    .also { print("${it.last + 1}d10:(") }
                    .sumOf { Random.nextInt(range = 1..10).also { print("$it,") } }
                    .also { print(")+") }
                val d12 = (0 until dice.d12)
                    .also { print("${it.last + 1}d12:(") }
                    .sumOf { Random.nextInt(range = 1..12).also { print("$it,") } }
                    .also { print(")+") }
                val d20 = (0 until dice.d20)
                    .also { print("${it.last + 1}d20:(") }
                    .sumOf { Random.nextInt(range = 1..20).also { print("$it,") } }
                    .also { print(")+") }
                val d100 = (0 until dice.d100)
                    .also { print("${it.last + 1}d100:(") }
                    .sumOf { Random.nextInt(range = 1..100).also { print("$it,") } }
                    .also { println(")") }
                dices.value = dices.value.copy(
                    result = DiceMenuUio.Result(
                        id = UUID.randomUUID().toString(),
                        value = d4 + d6 + d8 + d10 + d12 + d20 + d100,
                    ),
                )
            }
        }
    )
}

@Composable
private fun DiceMenuContent(
    modifier: Modifier = Modifier,
    size: Dp = DiceMenuDefault.size,
    spacings: DpSize = DiceMenuDefault.spacings,
    paddingValues: PaddingValues = DiceMenuDefault.paddings,
    dices: State<DiceMenuUio>,
    onAddD4: () -> Unit,
    onRemoveD4: () -> Unit,
    onAddD6: () -> Unit,
    onRemoveD6: () -> Unit,
    onAddD8: () -> Unit,
    onRemoveD8: () -> Unit,
    onAddD10: () -> Unit,
    onRemoveD10: () -> Unit,
    onAddD12: () -> Unit,
    onRemoveD12: () -> Unit,
    onAddD20: () -> Unit,
    onRemoveD20: () -> Unit,
    onAddD100: () -> Unit,
    onRemoveD100: () -> Unit,
    onRoll: () -> Unit,
) {
    Column(
        modifier = Modifier
            .padding(paddingValues = paddingValues)
            .then(other = modifier),
        verticalArrangement = Arrangement.spacedBy(space = spacings.height),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Dice(
            painter = painterResource(Res.drawable.icon_d4),
            size = size,
            spacings = spacings.width,
            value = dices.value.d4,
            onAdd = onAddD4,
            onRemove = onRemoveD4,
        )
        Dice(
            painter = painterResource(Res.drawable.icon_d6),
            size = size,
            spacings = spacings.width,
            value = dices.value.d6,
            onAdd = onAddD6,
            onRemove = onRemoveD6,
        )
        Dice(
            painter = painterResource(Res.drawable.icon_d8),
            size = size,
            spacings = spacings.width,
            value = dices.value.d8,
            onAdd = onAddD8,
            onRemove = onRemoveD8,
        )
        Dice(
            painter = painterResource(Res.drawable.icon_d10),
            size = size,
            spacings = spacings.width,
            value = dices.value.d10,
            onAdd = onAddD10,
            onRemove = onRemoveD10,
        )
        Dice(
            painter = painterResource(Res.drawable.icon_d12),
            size = size,
            spacings = spacings.width,
            value = dices.value.d12,
            onAdd = onAddD12,
            onRemove = onRemoveD12,
        )
        Dice(
            painter = painterResource(Res.drawable.icon_d20),
            size = size,
            spacings = spacings.width,
            value = dices.value.d20,
            onAdd = onAddD20,
            onRemove = onRemoveD20,
        )
        Dice(
            painter = painterResource(Res.drawable.icon_d100),
            size = size,
            spacings = spacings.width,
            value = dices.value.d100,
            postSign = "=",
            onAdd = onAddD100,
            onRemove = onRemoveD100,
        )
        AnimatedContent(
            modifier = Modifier.padding(top = spacings.height),
            targetState = dices.value.result,
            transitionSpec = {
                val enter = fadeIn() + slideInVertically { -16 }
                val exit = fadeOut() + slideOutVertically { 16 }
                enter togetherWith exit using SizeTransform(clip = false)
            },
        ) {
            Text(
                modifier = Modifier.fillMaxWidth(),
                style = MaterialTheme.lwa.typography.freeDiceThrow.result,
                textAlign = TextAlign.Center,
                text = "${it?.value ?: ""}"
            )
        }
        TextButton(
            modifier = Modifier.align(alignment = Alignment.CenterHorizontally),
            colors = LwaButtonColors(),
            onClick = onRoll,
        ) {
            Text(
                text = "Roll"
            )
        }
    }
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun Dice(
    modifier: Modifier = Modifier,
    size: Dp,
    spacings: Dp,
    painter: Painter,
    value: Int,
    postSign: String = "+",
    onAdd: () -> Unit,
    onRemove: () -> Unit,
) {
    Row(
        modifier = Modifier
            .onClick(
                matcher = PointerMatcher.mouse(PointerButton.Primary),
                onClick = onAdd,
            )
            .onClick(
                matcher = PointerMatcher.mouse(PointerButton.Secondary),
                onClick = onRemove,
            )
            .then(
                other = modifier,
            ),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(space = spacings),
    ) {
        Image(
            modifier = Modifier.size(size),
            painter = painter,
            contentDescription = null,
        )
        Text(
            style = MaterialTheme.lwa.typography.freeDiceThrow.dice,
            text = "×"
        )
        AnimatedContent(
            targetState = value,
            transitionSpec = { contentTransform() }
        ) {
            Text(
                style = MaterialTheme.lwa.typography.freeDiceThrow.dice,
                text = "$it"
            )
        }
        Text(
            style = MaterialTheme.lwa.typography.freeDiceThrow.dice,
            text = postSign,
        )
    }
}

private fun AnimatedContentTransitionScope<Int>.contentTransform(): ContentTransform {
    val sign = if (initialState < targetState) 1 else -1
    val enter = fadeIn() + slideInVertically { -16 * sign }
    val exit = fadeOut() + slideOutVertically { 16 * sign }
    return enter togetherWith exit using SizeTransform(clip = false)
}