package com.github.nullptroma.wallenc.ui.screens.main

import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Cloud
import androidx.compose.material.icons.outlined.Folder
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.toRoute
import com.github.nullptroma.wallenc.ui.R
import com.github.nullptroma.wallenc.ui.elements.AnimatedFloatingBackButton
import com.github.nullptroma.wallenc.ui.elements.FloatingWallencNavigationBar
import com.github.nullptroma.wallenc.ui.elements.WallencNestedNavBarGap
import com.github.nullptroma.wallenc.ui.navigation.NavBarItemData
import com.github.nullptroma.wallenc.ui.navigation.NavigationState
import com.github.nullptroma.wallenc.ui.navigation.isMainTopLevelRoute
import com.github.nullptroma.wallenc.ui.navigation.isTextEditDestination
import com.github.nullptroma.wallenc.ui.navigation.shouldShowMainFloatingBack
import com.github.nullptroma.wallenc.ui.navigation.rememberNavigationState
import com.github.nullptroma.wallenc.ui.screens.main.screens.remotes.RemoteVaultsRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.remotes.RemoteVaultsScreen
import com.github.nullptroma.wallenc.ui.screens.main.screens.remotes.RemoteVaultsViewModel
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.StorageHomeRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.StorageHomeScreen
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.secrets.TextSecretDetailsRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.secrets.TextSecretDetailsScreen
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.secrets.TextSecretEditRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.secrets.TextSecretEditScreen
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.secrets.TextSecretsRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.secrets.TextSecretsScreen
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.twofa.TwoFaTokensRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.twofa.TwoFaTokensScreen
import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.LocalVaultRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.LocalVaultScreen
import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.LocalVaultViewModel
import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.RemoteVaultViewModel
import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.VaultBrowserRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.VaultBrowserScreen
import com.github.nullptroma.wallenc.ui.screens.shared.TextEditRoute
import com.github.nullptroma.wallenc.ui.screens.shared.TextEditScreen

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
    modifier: Modifier = Modifier,
    viewModel: MainViewModel = hiltViewModel(),
    navState: NavigationState = rememberNavigationState(),
) {
    val routes = viewModel.routes
    val mainUi by viewModel.state.collectAsStateWithLifecycle()
    val localVaultViewModel: LocalVaultViewModel = hiltViewModel()
    val remoteVaultsViewModel: RemoteVaultsViewModel = hiltViewModel()

    val childBackStackEntry by navState.navHostController.currentBackStackEntryAsState()
    val childRoute = childBackStackEntry?.destination?.route
    val showWorkStatusBar = !isTextEditDestination(childRoute)
    val showMainBottomNav = isMainTopLevelRoute(childRoute)
    val showFloatingBack = shouldShowMainFloatingBack(childRoute) && navState.canPop()
    val workStatus = mainUi.workStatus
    val onBack: () -> Unit = { navState.pop() }

    val topLevelNavBarItems = remember {
        mapOf(
            LocalVaultRoute::class.qualifiedName!! to NavBarItemData(
                nameStringResourceId = R.string.nav_label_local_vault,
                screenRouteClass = LocalVaultRoute::class.qualifiedName!!,
                icon = Icons.Outlined.Folder,
                iconContentDescriptionResourceId = R.string.nav_cd_local_vault,
            ),
            RemoteVaultsRoute::class.qualifiedName!! to NavBarItemData(
                nameStringResourceId = R.string.nav_label_remote_vaults,
                screenRouteClass = RemoteVaultsRoute::class.qualifiedName!!,
                icon = Icons.Outlined.Cloud,
                iconContentDescriptionResourceId = R.string.nav_cd_remote_vaults,
            ),
        )
    }

    Scaffold(
        modifier = modifier,
        contentWindowInsets = WindowInsets(0.dp),
        topBar = {
            if (showWorkStatusBar) {
                MainWorkStatusBar(status = workStatus)
            }
        },
        bottomBar = {
            if (showMainBottomNav) {
                Box(
                    modifier = Modifier
                        .padding(horizontal = 16.dp)
                        .padding(top = WallencNestedNavBarGap, bottom = WallencNestedNavBarGap),
                ) {
                    FloatingWallencNavigationBar(
                        compact = true,
                        items = topLevelNavBarItems,
                        routes = routes,
                        currentRoute = childRoute,
                        onNavigate = { item ->
                            val route = routes[item.screenRouteClass]
                                ?: error("Route ${item.screenRouteClass} not found")
                            navState.changeTop(route)
                        },
                    )
                }
            }
        },
    ) { innerPaddings ->
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(innerPaddings),
        ) {
            NavHost(
                navController = navState.navHostController,
                startDestination = routes[LocalVaultRoute::class.qualifiedName]!!,
                modifier = Modifier.fillMaxSize(),
            ) {
            composable<LocalVaultRoute>(
                enterTransition = { fadeIn(tween(200)) },
                exitTransition = { fadeOut(tween(200)) },
            ) {
                LocalVaultScreen(
                    viewModel = localVaultViewModel,
                    onOpenStorageHome = { storageUuid ->
                        navState.push(StorageHomeRoute(storageUuid = storageUuid))
                    },
                )
            }
            composable<RemoteVaultsRoute>(
                enterTransition = { fadeIn(tween(200)) },
                exitTransition = { fadeOut(tween(200)) },
            ) {
                RemoteVaultsScreen(
                    viewModel = remoteVaultsViewModel,
                    onOpenVault = { item ->
                        navState.push(VaultBrowserRoute(item.uuid.toString()))
                    },
                )
            }
            composable<VaultBrowserRoute>(
                enterTransition = { fadeIn(tween(200)) },
                exitTransition = { fadeOut(tween(200)) },
            ) { entry ->
                val remoteVaultViewModel: RemoteVaultViewModel = hiltViewModel(entry)
                val route: VaultBrowserRoute = entry.toRoute()
                VaultBrowserScreen(
                    viewModel = remoteVaultViewModel,
                    onOpenStorageHome = { storageUuid ->
                        navState.push(
                            StorageHomeRoute(
                                vaultUuid = route.vaultUuid,
                                storageUuid = storageUuid,
                            ),
                        )
                    },
                )
            }
            composable<StorageHomeRoute>(
                enterTransition = { fadeIn(tween(200)) },
                exitTransition = { fadeOut(tween(200)) },
            ) {
                StorageHomeScreen(
                    onOpenTwoFa = { storageUuid ->
                        navState.push(TwoFaTokensRoute(storageUuid))
                    },
                    onOpenTextSecrets = { storageUuid ->
                        navState.push(TextSecretsRoute(storageUuid))
                    },
                )
            }
            composable<TwoFaTokensRoute>(
                enterTransition = { fadeIn(tween(200)) },
                exitTransition = { fadeOut(tween(200)) },
            ) {
                TwoFaTokensScreen()
            }
            composable<TextSecretsRoute>(
                enterTransition = { fadeIn(tween(200)) },
                exitTransition = { fadeOut(tween(200)) },
            ) { entry ->
                val route: TextSecretsRoute = entry.toRoute()
                TextSecretsScreen(
                    onOpenSecret = { secret ->
                        navState.push(
                            TextSecretDetailsRoute(
                                storageUuid = route.storageUuid,
                                secretId = secret.id,
                            ),
                        )
                    },
                    onCreateSecret = {
                        navState.push(
                            TextSecretEditRoute(
                                storageUuid = route.storageUuid,
                                secretId = null,
                            ),
                        )
                    },
                )
            }
            composable<TextSecretDetailsRoute>(
                enterTransition = { fadeIn(tween(200)) },
                exitTransition = { fadeOut(tween(200)) },
            ) { entry ->
                val route: TextSecretDetailsRoute = entry.toRoute()
                TextSecretDetailsScreen(
                    onEdit = { secretId ->
                        navState.push(
                            TextSecretEditRoute(
                                storageUuid = route.storageUuid,
                                secretId = secretId,
                            ),
                        )
                    },
                    onDeleted = { navState.pop() },
                )
            }
            composable<TextSecretEditRoute>(
                enterTransition = { fadeIn(tween(200)) },
                exitTransition = { fadeOut(tween(200)) },
            ) { entry ->
                val route: TextSecretEditRoute = entry.toRoute()
                TextSecretEditScreen(
                    onSaved = { savedSecretId ->
                        val editingExisting = route.secretId != null
                        navState.pop()
                        if (!editingExisting) {
                            navState.push(
                                TextSecretDetailsRoute(
                                    storageUuid = route.storageUuid,
                                    secretId = savedSecretId,
                                ),
                            )
                        }
                    },
                )
            }
            composable<TextEditRoute> {
                val route: TextEditRoute = it.toRoute()
                TextEditScreen(text = route.text)
            }
            }
            AnimatedFloatingBackButton(
                visible = showFloatingBack,
                onClick = onBack,
                modifier = Modifier
                    .zIndex(1f)
                    .align(Alignment.BottomStart)
                    .navigationBarsPadding()
                    .padding(start = 12.dp, bottom = 12.dp),
            )
        }
    }
}
