package com.pixelized.desktop.lwa.ui.overlay.roll

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.animateFloatAsState
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.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ExposedDropdownMenuBox
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.overlay.roll.CriticalityUio.Criticality
import com.pixelized.desktop.lwa.ui.overlay.roll.DifficultyUio.Difficulty
import com.pixelized.desktop.lwa.ui.theme.lwa
import com.pixelized.desktop.lwa.utils.DisableInteractionSource
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
import lwacharactersheet.composeapp.generated.resources.ic_share_24dp
import lwacharactersheet.composeapp.generated.resources.ic_share_off_24dp
import lwacharactersheet.composeapp.generated.resources.roll_page__dc__label
import lwacharactersheet.composeapp.generated.resources.roll_page__dc_easy__label
import lwacharactersheet.composeapp.generated.resources.roll_page__dc_hard__label
import lwacharactersheet.composeapp.generated.resources.roll_page__dc_impossible__label
import lwacharactersheet.composeapp.generated.resources.roll_page__dc_normal__label
import lwacharactersheet.composeapp.generated.resources.roll_page__roll__label
import lwacharactersheet.composeapp.generated.resources.roll_page__roll__success_label
import lwacharactersheet.composeapp.generated.resources.roll_page__criticality__label
import lwacharactersheet.composeapp.generated.resources.roll_page__criticality_normal__label
import lwacharactersheet.composeapp.generated.resources.roll_page__criticality_special__label
import lwacharactersheet.composeapp.generated.resources.roll_page__criticality_critical__label
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource

@Stable
data class RollTitleUio(
    val label: String,
    val value: Int?,
)

@Stable
data class RollResultUio(
    val label: String,
    val value: Int,
)

@Stable
data class DifficultyUio(
    val open: Boolean,
    val difficulty: Difficulty,
) {
    @Stable
    enum class Difficulty {
        EASY, NORMAL, HARD, IMPOSSIBLE
    }
}

@Stable
data class CriticalityUio(
    val open: Boolean,
    val criticality: Criticality,
) {
    enum class Criticality {
        NORMAL, SPECIAL, CRITICAL
    }
}

@Composable
fun RollPage(
    modifier: Modifier = Modifier,
    viewModel: RollViewModel,
    onDismissRequest: () -> Unit,
    onRoll: () -> Unit,
) {
    Box(
        modifier = Modifier.fillMaxSize()
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = null,
                onClick = onDismissRequest,
            )
            .padding(all = 16.dp)
            .then(other = modifier),
    ) {
        viewModel.rollTitle.value?.let {
            Column(
                modifier = Modifier.fillMaxWidth().padding(top = 16.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
            ) {
                Text(
                    style = MaterialTheme.typography.caption,
                    textAlign = TextAlign.Center,
                    overflow = TextOverflow.Ellipsis,
                    text = stringResource(Res.string.roll_page__roll__label),
                )
                Text(
                    style = MaterialTheme.typography.h5,
                    textAlign = TextAlign.Center,
                    overflow = TextOverflow.Ellipsis,
                    text = it.label,
                )
                if (it.value != null) {
                    Text(
                        style = MaterialTheme.typography.caption,
                        textAlign = TextAlign.Center,
                        overflow = TextOverflow.Ellipsis,
                        text = stringResource(Res.string.roll_page__roll__success_label, it.value),
                    )
                }
            }
        }

        IconButton(
            modifier = Modifier.align(alignment = Alignment.TopEnd),
            onClick = { viewModel.toggleShareResult() },
        ) {
            Icon(
                painter = when (viewModel.shareResult.value) {
                    true -> painterResource(Res.drawable.ic_share_24dp)
                    else -> painterResource(Res.drawable.ic_share_off_24dp)
                },
                tint = MaterialTheme.lwa.colorScheme.base.primary,
                contentDescription = null
            )
        }

        Column(
            modifier = Modifier.matchParentSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.spacedBy(
                space = 24.dp,
                alignment = Alignment.CenterVertically,
            ),
        ) {
            Box(
                modifier = Modifier.graphicsLayer {
                    this.scaleX = viewModel.rollScale.value
                    this.scaleY = viewModel.rollScale.value
                },
                contentAlignment = Alignment.Center
            ) {
                Icon(
                    modifier = Modifier
                        .clip(shape = CircleShape)
                        .let {
                            when (viewModel.cancellable.value) {
                                true -> it.clickable(onClick = onRoll)
                                else -> it
                            }
                        }
                        .padding(all = 24.dp)
                        .size(size = 128.dp)
                        .graphicsLayer {
                            this.alpha = 0.8f
                            this.rotationZ = viewModel.rollRotation.value

                        },
                    tint = MaterialTheme.colors.primary,
                    painter = painterResource(Res.drawable.ic_d20_24dp),
                    contentDescription = null,
                )
                AnimatedContent(
                    targetState = viewModel.rollResult.value?.value?.toString() ?: "",
                    transitionSpec = {
                        val enter = fadeIn() + slideInVertically { -32 }
                        val exit = fadeOut() + slideOutVertically { 32 }
                        enter togetherWith exit using SizeTransform(clip = false)
                    },
                ) { label ->
                    Text(
                        modifier = Modifier.width(width = 128.dp),
                        style = MaterialTheme.typography.h3.copy(
                            shadow = Shadow(
                                color = MaterialTheme.colors.surface,
                                offset = Offset.Zero,
                                blurRadius = 8f,
                            )
                        ),
                        textAlign = TextAlign.Center,
                        color = MaterialTheme.colors.onSurface,
                        text = label,
                    )
                }
            }
            AnimatedContent(
                targetState = viewModel.rollResult.value?.label ?: "",
                transitionSpec = { fadeIn() togetherWith fadeOut() },
            ) { value ->
                Text(
                    modifier = Modifier.fillMaxWidth(),
                    style = MaterialTheme.typography.h4,
                    textAlign = TextAlign.Center,
                    text = value,
                )
            }
        }

        viewModel.rollDifficulty.value?.let {
            Box(
                modifier = Modifier
                    .align(Alignment.BottomCenter)
                    .padding(bottom = 16.dp)
                    .clickable(
                        interactionSource = remember { DisableInteractionSource() },
                        indication = null,
                        onClick = { },
                    )
            ) {
                Difficulty(
                    difficulty = it,
                    onToggle = { viewModel.toggleDifficulty() },
                    onDifficulty = { viewModel.onDifficulty(it) }
                )
            }
        }

        viewModel.rollCriticality.value?.let {
            Box(
                modifier = Modifier
                    .align(Alignment.BottomCenter)
                    .padding(bottom = 16.dp)
                    .clickable(
                        interactionSource = remember { DisableInteractionSource() },
                        indication = null,
                        onClick = { },
                    )
            ) {
                Criticality(
                    criticality = it,
                    onToggle = { viewModel.toggleCriticality() },
                    onCriticality = { viewModel.onCriticality(it) },
                )
            }
        }
    }
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun Difficulty(
    modifier: Modifier = Modifier,
    difficulty: DifficultyUio,
    onToggle: () -> Unit,
    onDifficulty: (Difficulty) -> Unit,
) {
    ExposedDropdownMenuBox(
        modifier = modifier,
        expanded = difficulty.open,
        onExpandedChange = { onToggle() },
    ) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Row(
                modifier = Modifier.padding(
                    horizontal = 16.dp,
                    vertical = 8.dp,
                ),
                horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
            ) {
                Text(
                    modifier = Modifier.alignByBaseline(),
                    style = MaterialTheme.typography.body1,
                    text = stringResource(Res.string.roll_page__dc__label)
                )
                AnimatedContent(
                    targetState = difficulty.difficulty,
                    transitionSpec = {
                        val enter = fadeIn() + slideInVertically { -16 }
                        val exit = fadeOut() + slideOutVertically { 16 }
                        enter togetherWith exit using SizeTransform(clip = false)
                    },
                ) { difficulty ->
                    Text(
                        modifier = Modifier.alignByBaseline().animateContentSize(),
                        style = MaterialTheme.typography.body1,
                        color = MaterialTheme.colors.primary,
                        text = when (difficulty) {
                            Difficulty.EASY -> stringResource(Res.string.roll_page__dc_easy__label)
                            Difficulty.NORMAL -> stringResource(Res.string.roll_page__dc_normal__label)
                            Difficulty.HARD -> stringResource(Res.string.roll_page__dc_hard__label)
                            Difficulty.IMPOSSIBLE -> stringResource(Res.string.roll_page__dc_impossible__label)
                        }
                    )
                }
            }
            val rotation = animateFloatAsState(
                targetValue = if (difficulty.open) -180f else 0f,
            )
            Icon(
                modifier = Modifier
                    .offset(x = (-12).dp)
                    .graphicsLayer { rotationZ = rotation.value },
                imageVector = Icons.Default.ArrowDropDown,
                contentDescription = null
            )
        }

        ExposedDropdownMenu(
            expanded = difficulty.open,
            onDismissRequest = { onToggle() }
        ) {
            LocalDropDownItem(
                label = stringResource(Res.string.roll_page__dc_easy__label),
                onClick = { onDifficulty(Difficulty.EASY) },
            )
            LocalDropDownItem(
                label = stringResource(Res.string.roll_page__dc_normal__label),
                onClick = { onDifficulty(Difficulty.NORMAL) },
            )
            LocalDropDownItem(
                label = stringResource(Res.string.roll_page__dc_hard__label),
                onClick = { onDifficulty(Difficulty.HARD) },
            )
            LocalDropDownItem(
                label = stringResource(Res.string.roll_page__dc_impossible__label),
                onClick = { onDifficulty(Difficulty.IMPOSSIBLE) },
            )
        }
    }
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun Criticality(
    modifier: Modifier = Modifier,
    criticality: CriticalityUio,
    onToggle: () -> Unit,
    onCriticality: (Criticality) -> Unit,
) {
    ExposedDropdownMenuBox(
        modifier = modifier,
        expanded = criticality.open,
        onExpandedChange = { onToggle() },
    ) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Row(
                modifier = Modifier.padding(
                    horizontal = 16.dp,
                    vertical = 8.dp,
                ),
                horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
            ) {
                Text(
                    modifier = Modifier.alignByBaseline(),
                    style = MaterialTheme.typography.body1,
                    text = stringResource(Res.string.roll_page__criticality__label)
                )
                AnimatedContent(
                    targetState = criticality.criticality,
                    transitionSpec = {
                        val enter = fadeIn() + slideInVertically { -16 }
                        val exit = fadeOut() + slideOutVertically { 16 }
                        enter togetherWith exit using SizeTransform(clip = false)
                    },
                ) {
                    Text(
                        modifier = Modifier.alignByBaseline().animateContentSize(),
                        style = MaterialTheme.typography.body1,
                        color = MaterialTheme.colors.primary,
                        text = when (it) {
                            Criticality.NORMAL -> stringResource(Res.string.roll_page__criticality_normal__label)
                            Criticality.SPECIAL -> stringResource(Res.string.roll_page__criticality_special__label)
                            Criticality.CRITICAL -> stringResource(Res.string.roll_page__criticality_critical__label)
                        }
                    )
                }
            }
            val rotation = animateFloatAsState(
                targetValue = if (criticality.open) -180f else 0f,
            )
            Icon(
                modifier = Modifier
                    .offset(x = (-12).dp)
                    .graphicsLayer { rotationZ = rotation.value },
                imageVector = Icons.Default.ArrowDropDown,
                contentDescription = null
            )
        }

        ExposedDropdownMenu(
            expanded = criticality.open,
            onDismissRequest = { onToggle() }
        ) {
            LocalDropDownItem(
                label = stringResource(Res.string.roll_page__criticality_normal__label),
                onClick = { onCriticality(Criticality.NORMAL) },
            )
            LocalDropDownItem(
                label = stringResource(Res.string.roll_page__criticality_special__label),
                onClick = { onCriticality(Criticality.SPECIAL) },
            )
            LocalDropDownItem(
                label = stringResource(Res.string.roll_page__criticality_critical__label),
                onClick = { onCriticality(Criticality.CRITICAL) },
            )
        }
    }
}

@Composable
private fun LocalDropDownItem(
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp),
    label: String,
    onClick: () -> Unit,
) {
    DropdownMenuItem(
        modifier = modifier,
        onClick = onClick,
        enabled = true,
        contentPadding = contentPadding,
        interactionSource = remember { MutableInteractionSource() },
        content = {
            Text(
                color = MaterialTheme.colors.primary,
                text = label,
            )
        },
    )
}