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

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
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.ScrollState
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.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.SnackbarDuration
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.LocalSnackHost
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
import com.pixelized.desktop.lwa.ui.theme.lwa
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp
import lwacharactersheet.composeapp.generated.resources.network__host__label
import lwacharactersheet.composeapp.generated.resources.network__player_name__label
import lwacharactersheet.composeapp.generated.resources.network__port__label
import lwacharactersheet.composeapp.generated.resources.network__socket__connect_action
import lwacharactersheet.composeapp.generated.resources.network__socket__disconnect_action
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel

@Stable
data class NetworkPageUio(
    val player: String,
    val host: String,
    val resetHost: Boolean,
    val port: String,
    val resetPort: Boolean,
    val enableFields: Boolean,
    val enableActions: Boolean,
    val enableCancel: Boolean,
) {
    companion object {
        @Stable
        fun empty(
            player: String = "",
            host: String = "",
            resetHost: Boolean = false,
            port: String = "",
            resetPort: Boolean = false,
            enableFields: Boolean = false,
            enableActions: Boolean = false,
            enableCancel: Boolean = false,
        ) = NetworkPageUio(
            player = player,
            host = host,
            resetHost = resetHost,
            port = port,
            resetPort = resetPort,
            enableFields = enableFields,
            enableActions = enableActions,
            enableCancel = enableCancel,
        )
    }
}

@Composable
fun NetworkMenu(
    modifier: Modifier = Modifier,
    viewModel: NetworkViewModel = koinViewModel(),
) {
    val snack = LocalSnackHost.current
    val scope = rememberCoroutineScope()

    Box(
        modifier = modifier,
    ) {
        BlurContent(
            modifier = Modifier.fillMaxSize(),
            controller = viewModel.blurController,
        ) {
            Surface {
                NetworkContent(
                    modifier = Modifier.fillMaxSize(),
                    network = viewModel.network.collectAsState(),
                    onPlayerChange = viewModel::onPlayerNameChange,
                    onHostChange = viewModel::onHostChange,
                    onResetHostChange = viewModel::onResetHostChange,
                    onPortChange = viewModel::onPortChange,
                    onResetPortChange = viewModel::onResetPortChange,
                    onConnect = { scope.launch { viewModel.connect() } },
                    onDisconnect = viewModel::disconnect,
                )
            }
        }
        AnimatedContent(
            modifier = Modifier
                .align(alignment = Alignment.Center)
                .size(size = 32.dp),
            targetState = viewModel.isLoading.value,
            transitionSpec = {
                val enter = fadeIn() + slideInVertically { 64 }
                val exit = fadeOut() + slideOutVertically { 64 }
                enter togetherWith exit using SizeTransform(clip = false)
            },
        ) {
            when (it) {
                true -> CircularProgressIndicator()
                else -> Box(modifier = Modifier)
            }
        }
    }

    LaunchedEffect(Unit) {
        viewModel.message.collect {
            snack.showSnackbar(
                message = it,
                duration = SnackbarDuration.Short,
            )
        }
    }

    ErrorSnackHandler(
        error = viewModel.networkError,
    )
}

@Composable
private fun NetworkContent(
    modifier: Modifier = Modifier,
    scrollState: ScrollState = rememberScrollState(),
    paddingValues: PaddingValues = PaddingValues(),
    network: State<NetworkPageUio>,
    onPlayerChange: (String) -> Unit,
    onHostChange: (String) -> Unit,
    onResetPortChange: () -> Unit,
    onPortChange: (String) -> Unit,
    onResetHostChange: () -> Unit,
    onConnect: () -> Unit,
    onDisconnect: () -> Unit,
) {
    Column(
        modifier = Modifier
            .verticalScroll(state = scrollState)
            .padding(paddingValues)
            .padding(all = 16.dp)
            .then(other = modifier),
    ) {
        TextField(
            modifier = Modifier.fillMaxWidth(),
            singleLine = true,
            enabled = network.value.enableFields,
            label = { Text(text = stringResource(Res.string.network__player_name__label)) },
            onValueChange = { onPlayerChange(it) },
            value = network.value.player,
        )

        Spacer(
            modifier = Modifier.height(16.dp),
        )

        Row(
            horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
        ) {
            TextField(
                modifier = Modifier.weight(1f),
                singleLine = true,
                enabled = network.value.enableFields,
                label = { Text(text = stringResource(Res.string.network__host__label)) },
                trailingIcon = {
                    AnimatedVisibility(
                        visible = network.value.resetHost,
                        enter = fadeIn(),
                        exit = fadeOut(),
                    ) {
                        IconButton(
                            onClick = onResetHostChange,
                        ) {
                            Icon(
                                painter = painterResource(Res.drawable.ic_cancel_24dp),
                                tint = MaterialTheme.lwa.colorScheme.base.primary,
                                contentDescription = null,
                            )
                        }
                    }
                },
                onValueChange = { onHostChange(it) },
                value = network.value.host,
            )
            TextField(
                modifier = Modifier.width(124.dp),
                singleLine = true,
                enabled = network.value.enableFields,
                label = { Text(text = stringResource(Res.string.network__port__label)) },
                trailingIcon = {
                    AnimatedVisibility(
                        visible = network.value.resetPort,
                        enter = fadeIn(),
                        exit = fadeOut(),
                    ) {
                        IconButton(
                            onClick = onResetPortChange,
                        ) {
                            Icon(
                                painter = painterResource(Res.drawable.ic_cancel_24dp),
                                tint = MaterialTheme.lwa.colorScheme.base.primary,
                                contentDescription = null,
                            )
                        }
                    }
                },
                onValueChange = { onPortChange(it) },
                value = network.value.port,
            )
        }

        TextButton(
            modifier = Modifier.align(alignment = Alignment.End),
            enabled = network.value.enableActions,
            onClick = onConnect,
        ) {
            Text(text = stringResource(Res.string.network__socket__connect_action))
        }

        TextButton(
            modifier = Modifier.align(alignment = Alignment.End),
            enabled = network.value.enableCancel,
            onClick = onDisconnect,
        ) {
            Text(text = stringResource(Res.string.network__socket__disconnect_action))
        }
    }
}