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

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.isAltPressed
import androidx.compose.ui.input.key.isCtrlPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentController
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialog
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialog
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToLevelScreen
import com.pixelized.desktop.lwa.ui.overlay.portrait.PortraitOverlay
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanelViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.DetailPanelUio
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.rememberTransitionAnimation
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbon
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.NpcRibbonViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbonViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.CampaignChat
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.CampaignChatViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbar
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbarViewModel
import com.pixelized.desktop.lwa.ui.theme.lwa
import kotlinx.coroutines.launch
import org.koin.compose.viewmodel.koinViewModel

val LocalCampaignLayoutScope = compositionLocalOf<CampaignLayoutScope> {
    error("LocalCampaignLayoutScope is not yet ready.")
}

@Composable
fun CampaignScreen(
    playerRibbonViewModel: PlayerRibbonViewModel = koinViewModel(),
    playerDetailViewModel: CharacterDetailPanelViewModel = koinViewModel(key = "player"),
    npcRibbonViewModel: NpcRibbonViewModel = koinViewModel(),
    npcDetailViewModel: CharacterDetailPanelViewModel = koinViewModel(key = "npc"),
    characteristicDialogViewModel: CharacterSheetCharacteristicDialogViewModel = koinViewModel(),
    dismissedViewModel: CharacterSheetDiminishedDialogViewModel = koinViewModel(),
    alterationViewModel: CharacterSheetAlterationDialogViewModel = koinViewModel(),
    campaignViewModel: CampaignToolbarViewModel = koinViewModel(),
    campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
) {
    val screen = LocalScreenController.current
    val blurController = rememberBlurContentController()
    val scope = rememberCoroutineScope()

    Surface(
        modifier = Modifier.fillMaxSize(),
    ) {
        BlurContent(
            modifier = Modifier.fillMaxSize(),
            controller = blurController
        ) {
            CampaignLayout(
                modifier = Modifier.fillMaxSize(),
                top = {
                    CampaignToolbar(
                        toolBarViewModel = campaignViewModel,
                    )
                },
                bottom = {

                },
                main = {

                },
                chat = {
                    CampaignChat(
                        modifier = Modifier.padding(vertical = MaterialTheme.lwa.dimen.paddingValue),
                        chatViewModel = campaignChatViewModel,
                    )
                },
                overlay = {
                    Box(
                        modifier = Modifier.matchParentSize(),
                        contentAlignment = Alignment.Center,
                    ) {
                        PortraitOverlay(
                            modifier = Modifier.padding(paddingValues = MaterialTheme.lwa.dimen.paddingValues),
                        )
                    }
                },
                leftPanel = {
                    CharacterRibbon(
                        modifier = Modifier.fillMaxHeight(),
                        viewModel = playerRibbonViewModel,
                        layoutDirection = LayoutDirection.Ltr,
                        onCharacterLeftClick = {
                            scope.launch {
                                playerDetailViewModel.showCharacter(
                                    characterSheetId = it,
                                    panel = DetailPanelUio.Sheet,
                                )
                            }
                        },
                        onCharacterRightClick = {
                            scope.launch {
                                playerDetailViewModel.showCharacter(
                                    characterSheetId = it,
                                    panel = DetailPanelUio.Inventory,
                                )
                            }
                        },
                    )
                },
                rightPanel = {
                    CharacterRibbon(
                        modifier = Modifier.fillMaxHeight(),
                        viewModel = npcRibbonViewModel,
                        layoutDirection = LayoutDirection.Rtl,
                        onCharacterLeftClick = {
                            scope.launch {
                                npcDetailViewModel.showCharacter(
                                    characterSheetId = it,
                                    panel = DetailPanelUio.Sheet,
                                )
                            }
                        },
                        onCharacterRightClick = {
                            scope.launch {
                                npcDetailViewModel.showCharacter(
                                    characterSheetId = it,
                                    panel = DetailPanelUio.Inventory,
                                )
                            }
                        },
                    )
                },
                leftOverlay = {
                    CharacterDetailPanel(
                        modifier = Modifier
                            .padding(paddingValues = MaterialTheme.lwa.dimen.paddingValues)
                            .padding(start = MaterialTheme.lwa.dimen.portrait.minimized.width + MaterialTheme.lwa.dimen.paddingValue)
                            .width(width = MaterialTheme.lwa.dimen.layout.detailWidth)
                            .fillMaxHeight(),
                        transitionSpec = rememberTransitionAnimation(direction = LayoutDirection.Ltr),
                        detailPanelViewModel = playerDetailViewModel,
                        characterDiminishedViewModel = dismissedViewModel,
                        characteristicDialogViewModel = characteristicDialogViewModel,
                        alterationViewModel = alterationViewModel,
                        onLevelUp = {
                            screen.navigateToLevelScreen(characterSheetId = it)
                        }
                    )
                },
                rightOverlay = {
                    CharacterDetailPanel(
                        modifier = Modifier
                            .padding(paddingValues = MaterialTheme.lwa.dimen.paddingValues)
                            .padding(end = MaterialTheme.lwa.dimen.portrait.minimized.width + MaterialTheme.lwa.dimen.paddingValue)
                            .width(width = MaterialTheme.lwa.dimen.layout.detailWidth)
                            .fillMaxHeight(),
                        transitionSpec = rememberTransitionAnimation(direction = LayoutDirection.Rtl),
                        detailPanelViewModel = npcDetailViewModel,
                        characterDiminishedViewModel = dismissedViewModel,
                        characteristicDialogViewModel = characteristicDialogViewModel,
                        alterationViewModel = alterationViewModel,
                        onLevelUp = {
                            screen.navigateToLevelScreen(characterSheetId = it)
                        }
                    )
                },
            )
        }

        CharacterSheetCharacteristicDialog(
            dialog = characteristicDialogViewModel.statChangeDialog,
            onConfirm = { dialog ->
                scope.launch {
                    characteristicDialogViewModel.changeSubCharacteristic(
                        characterSheetId = dialog.characterSheetId,
                        characteristic = dialog.characteristic,
                        useArmor = dialog.enableArmor?.checked?.value == true,
                        value = dialog.value.valueFlow.value.toIntOrNull() ?: 0,
                    )
                    characteristicDialogViewModel.hideSubCharacteristicDialog()
                    blurController.hide()
                }
            },
            onSwapSign = {
                characteristicDialogViewModel.swapCharacteristicSign()
            },
            onDismissRequest = {
                characteristicDialogViewModel.hideSubCharacteristicDialog()
                blurController.hide()
            },
        )

        CharacterSheetDiminishedDialog(
            dialog = dismissedViewModel.diminishedDialog,
            onConfirm = { diminished ->
                scope.launch {
                    dismissedViewModel.changeDiminished(
                        dialog = diminished
                    )
                    blurController.hide()
                    dismissedViewModel.hideDiminishedDialog()
                }
            },
            onDismissRequest = {
                blurController.hide()
                dismissedViewModel.hideDiminishedDialog()
            },
        )
    }

    CharacterSheetAlterationDialog(
        dialog = alterationViewModel.alterationDialog.collectAsState(),
        onTag = {
            alterationViewModel.selectedTag(id = it)
        },
        onAlteration = { characterSheetId, alterationId, active ->
            scope.launch {
                alterationViewModel.toggleAlteration(characterSheetId, alterationId, active)
            }
        },
        onDismissRequest = {
            blurController.hide()
            alterationViewModel.hide()
        },
    )

    CampaignKeyHandler(
        onDismissRequest = {
            val noDialog = characteristicDialogViewModel.statChangeDialog.value == null
                    && dismissedViewModel.diminishedDialog.value == null
                    && alterationViewModel.alterationDialog.value == null
            if (noDialog) {
                playerDetailViewModel.hideCharacter()
                npcDetailViewModel.hideCharacter()
            }
        },
        onPlayerNumber = {
            scope.launch {
                val characterSheetId = playerRibbonViewModel.characters(index = it) ?: return@launch
                npcDetailViewModel.hideCharacter()
                playerDetailViewModel.showCharacter(
                    characterSheetId = characterSheetId,
                    panel = DetailPanelUio.Sheet,
                )
            }
        },
        onAltPLayerNumber = {
            scope.launch {
                val characterSheetId = playerRibbonViewModel.characters(index = it) ?: return@launch
                npcDetailViewModel.hideCharacter()
                playerDetailViewModel.showCharacter(
                    characterSheetId = characterSheetId,
                    panel = DetailPanelUio.Inventory,
                )
            }
        }
    )

    ErrorSnackHandler(
        error = alterationViewModel.errors,
    )
}

@Composable
private fun CampaignLayout(
    modifier: Modifier = Modifier,
    top: @Composable () -> Unit,
    bottom: @Composable () -> Unit,
    main: @Composable () -> Unit,
    chat: @Composable () -> Unit,
    overlay: @Composable () -> Unit,
    leftPanel: @Composable () -> Unit,
    rightPanel: @Composable () -> Unit,
    leftOverlay: @Composable () -> Unit,
    rightOverlay: @Composable () -> Unit,
) {
    val density = LocalDensity.current

    val mainState = remember { mutableStateOf(DpSize.Unspecified) }
    val leftOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
    val leftPanelState = remember { mutableStateOf(DpSize.Unspecified) }
    val rightOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
    val rightPanelState = remember { mutableStateOf(DpSize.Unspecified) }
    val chatOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
    val scope = remember {
        CampaignLayoutScope(
            main = mainState,
            leftOverlay = leftOverlayState,
            leftPanel = leftPanelState,
            rightOverlay = rightOverlayState,
            rightPanel = rightPanelState,
            chatOverlay = chatOverlayState,
        )
    }

    CompositionLocalProvider(
        LocalCampaignLayoutScope provides scope,
    ) {
        Column(
            modifier = modifier,
        ) {
            top()

            Box(
                modifier = Modifier
                    .weight(weight = 1f, fill = true)
                    .onSizeChanged { mainState.value = it.toDp(density) },
            ) {
                main()

                Row {
                    Box(
                        modifier = Modifier
                            .width(width = MaterialTheme.lwa.dimen.layout.panelWidth)
                            .fillMaxHeight()
                            .onSizeChanged { leftPanelState.value = it.toDp(density) },
                    ) {
                        leftPanel()
                    }
                    Column(
                        modifier = Modifier.weight(weight = 1f, fill = true),
                    ) {
                        Box(
                            modifier = Modifier
                                .fillMaxWidth()
                                .weight(weight = 1f, fill = true),
                        ) {
                            overlay()
                        }
                        Box(
                            modifier = Modifier
                                .fillMaxWidth()
                                .onSizeChanged { chatOverlayState.value = it.toDp(density) },
                        ) {
                            chat()
                        }
                    }
                    Box(
                        modifier = Modifier
                            .width(width = MaterialTheme.lwa.dimen.layout.panelWidth)
                            .fillMaxHeight()
                            .onSizeChanged { rightPanelState.value = it.toDp(density) },
                    ) {
                        rightPanel()
                    }
                }
                Box(
                    modifier = Modifier
                        .align(alignment = Alignment.CenterStart)
                        .onSizeChanged { leftOverlayState.value = it.toDp(density) },
                ) {
                    leftOverlay()
                }
                Box(
                    modifier = Modifier
                        .align(alignment = Alignment.CenterEnd)
                        .onSizeChanged { rightOverlayState.value = it.toDp(density) },
                ) {
                    rightOverlay()
                }
            }
            bottom()
        }
    }
}

@Composable
private fun CampaignKeyHandler(
    onDismissRequest: () -> Unit,
    onPlayerNumber: (index: Int) -> Unit,
    onAltPLayerNumber: (index: Int) -> Unit,
) {
    fun KeyEvent.callback(index: Int) {
        if (isAltPressed) onAltPLayerNumber(index) else onPlayerNumber(index)
    }
    KeyHandler {
        if (it.type != KeyEventType.KeyDown) {
            return@KeyHandler false
        }
        if (it.key == Key.Escape) {
            onDismissRequest()
            return@KeyHandler true
        }
        if (it.isCtrlPressed.not()) {
            return@KeyHandler false
        }
        when (it.key) {
            Key.Escape -> onDismissRequest()
            Key.One, Key.NumPad1 -> it.callback(index = 0)
            Key.Two, Key.NumPad2 -> it.callback(index = 1)
            Key.Three, Key.NumPad3 -> it.callback(index = 2)
            Key.Four, Key.NumPad4 -> it.callback(index = 3)
            Key.Five, Key.NumPad5 -> it.callback(index = 4)
            Key.Six, Key.NumPad6 -> it.callback(index = 5)
            Key.Seven, Key.NumPad7 -> it.callback(index = 6)
            Key.Eight, Key.NumPad8 -> it.callback(index = 7)
            Key.Nine, Key.NumPad9 -> it.callback(index = 8)
            else -> return@KeyHandler false
        }
        return@KeyHandler true
    }
}

private fun IntSize.toDp(density: Density) = with(density) {
    DpSize(
        width = width.toDp(),
        height = height.toDp(),
    )
}

@Stable
data class CampaignLayoutScope(
    val main: State<DpSize>,
    val leftOverlay: State<DpSize>,
    val leftPanel: State<DpSize>,
    val rightOverlay: State<DpSize>,
    val rightPanel: State<DpSize>,
    val chatOverlay: State<DpSize>,
)
