package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.expandIn
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
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.heightIn
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
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.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import androidx.compose.ui.unit.min
import androidx.compose.ui.window.WindowState
import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindowState
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignLayoutScope
import com.pixelized.desktop.lwa.ui.screen.campaign.LocalCampaignLayoutScope
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.CharacteristicTextMessage
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.CharacteristicTextMessageUio
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.DiminishedTextMessage
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.DiminishedTextMessageUio
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.PurseTextMessage
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.PurseTextMessageUio
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.RollTextMessage
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.RollTextMessageUio
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.TextMessage
import com.pixelized.desktop.lwa.ui.theme.lwa
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_more_down_24dp
import org.jetbrains.compose.resources.painterResource
import org.koin.compose.viewmodel.koinViewModel

@Stable
data class ChatSettingsUio(
    val show: Boolean,
    val autoScroll: Boolean,
) {
    companion object {
        fun default() = with(SettingsUseCase().defaultSettings()) {
            ChatSettingsUio(
                show = chat.showChat,
                autoScroll = chat.autoScrollChat,
            )
        }
    }
}

@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
@Composable
fun CampaignChat(
    modifier: Modifier = Modifier,
    chatViewModel: CampaignChatViewModel = koinViewModel(),
) {
    val lazyState = rememberLazyListState()
    val campaignLayoutScope = LocalCampaignLayoutScope.current
    val colorScheme = MaterialTheme.lwa.colorScheme
    val messages = chatViewModel.messages.collectAsState()
    val settings = chatViewModel.settings.collectAsState()

    ChatScrollDownEffect(
        lazyState = lazyState,
        messages = messages,
        settings = settings,
    )

    Row(
        modifier = modifier.background(
            shape = MaterialTheme.lwa.shapes.chat,
            color = remember { colorScheme.elevated.base4dp.copy(alpha = 0.5f) },
        ),
    ) {
        AnimatedVisibility(
            visible = settings.value.show,
            enter = fadeIn() + expandIn(),
            exit = fadeOut() + shrinkOut(),
        ) {
            LazyColumn(
                modifier = Modifier
                    .width(
                        // 32.dp + 8.dp for the button and the space
                        width = min(
                            campaignLayoutScope.chatOverlay.value.width - 40.dp,
                            MaterialTheme.lwa.dimen.layout.chatMaxWidth
                        ),
                    )
                    .heightIn(
                        min = MaterialTheme.lwa.dimen.portrait.minimized.height,
                        max = MaterialTheme.lwa.dimen.portrait.minimized.height * 2 + MaterialTheme.lwa.dimen.paddingValue,
                    ),
                state = lazyState,
                verticalArrangement = Arrangement.spacedBy(
                    space = 4.dp,
                    alignment = Alignment.Bottom,
                ),
                contentPadding = remember { PaddingValues(all = 8.dp) },
            ) {
                items(
                    items = messages.value,
                    key = { it.id },
                    contentType = { it.javaClass.simpleName }
                ) {
                    when (it) {
                        is RollTextMessageUio -> RollTextMessage(message = it)
                        is PurseTextMessageUio -> PurseTextMessage(message = it)
                        is DiminishedTextMessageUio -> DiminishedTextMessage(message = it)
                        is CharacteristicTextMessageUio -> CharacteristicTextMessage(message = it)
                    }
                }
            }
        }
        Column {
            IconButton(
                modifier = Modifier.size(size = 32.dp),
                onClick = chatViewModel::toggleChat
            ) {
                val rotation = animateFloatAsState(
                    targetValue = if (settings.value.show) 0f else 180f,
                )
                Icon(
                    modifier = Modifier
                        .size(size = 16.dp)
                        .graphicsLayer {
                            this.rotationZ = rotation.value
                        },
                    painter = painterResource(Res.drawable.ic_more_down_24dp),
                    contentDescription = null,
                )
            }
        }
    }
}

@Composable
private fun ChatScrollDownEffect(
    lazyState: LazyListState,
    messages: State<List<TextMessage>>,
    settings: State<ChatSettingsUio>,
) {
    LaunchedEffect(
        key1 = messages.value.lastOrNull()?.id,
    ) {
        if (messages.value.isNotEmpty()) {
            if (settings.value.autoScroll) {
                lazyState.animateScrollToItem(
                    index = messages.value.lastIndex + 1,
                )
            }
        }
    }
}

@Composable
@Stable
private fun rememberAnimatedChatWidth(
    campaignScreenScope: CampaignLayoutScope = LocalCampaignLayoutScope.current,
    windowsState: WindowState = LocalWindowState.current,
    settings: State<ChatSettingsUio>,
): State<Dp> {
    val chatWidth = remember(windowsState, campaignScreenScope) {
        derivedStateOf {
            if (settings.value.show) {
                val minChatWidth = 64.dp * 8
                val maxChatWidth = 64.dp * 12
                val windowWidth = windowsState.size.width
                if (windowWidth != Dp.Unspecified) {
                    val width = windowWidth - campaignScreenScope.leftPanel.value.width - 16.dp
                    min(max(width, minChatWidth), maxChatWidth)
                } else {
                    minChatWidth
                }
            } else {
                0.dp
            }
        }
    }
    return animateDpAsState(
        targetValue = chatWidth.value,
    )
}