package moe.lava.neon.ui.screens.navigator

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeGestures
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialShapes
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SheetValue
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.material3.rememberStandardBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.unit.dp
import androidx.graphics.shapes.Morph
import kotlin.math.roundToInt

val guildIconSize = 56.dp

data class NavigatorModel(
    val guildNavPosition: GuildNavPosition,
    val guildName: String,
    val channels: List<Category>,
    val guilds: List<Guild>,
    val selected: Pair<Guild, Channel?>,
) {
    data class Category(
        val name: String,
        val channels: List<Channel>,
    )
    data class Guild(
        val name: String,
        val url: String,
    )
    data class Channel(
        val name: String,
        val type: ChannelType,
    )
    enum class ChannelType {
        Text,
        Voice
    }
    enum class GuildNavPosition {
        LeftSidebar,
        BottomSheet
    }
}

// TODO: Move channel's bottomSpace and padding into a Modifier, so this can be greatly simplified
// by sharing the Channels and GuildButton implementations in both layouts
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun Navigator(
    model: NavigatorModel
) {
    // TODO: this obviously should be done properly
    var selected by remember { mutableStateOf(model.selected) }
    val onNavigateChannel: (NavigatorModel.Channel) -> Unit = { ch -> selected = selected.copy(second = ch) }
    val onNavigateGuild: (NavigatorModel.Guild) -> Unit = { guild -> selected = selected.copy(first = guild) }

    when (model.guildNavPosition) {
        NavigatorModel.GuildNavPosition.BottomSheet -> BottomSheetNavigator(
            model.copy(selected = selected),
            onNavigateGuild = onNavigateGuild,
            onNavigateChannel = onNavigateChannel,
        )
        NavigatorModel.GuildNavPosition.LeftSidebar -> SidebarNavigator(
            model.copy(selected = selected),
            onNavigateGuild = onNavigateGuild,
            onNavigateChannel = onNavigateChannel,
        )
    }
}

@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun BottomSheetNavigator(
    model: NavigatorModel,
    onNavigateGuild: (NavigatorModel.Guild) -> Unit,
    onNavigateChannel: (NavigatorModel.Channel) -> Unit,
) {
    val morph = remember {
        Morph(MaterialShapes.Diamond, MaterialShapes.Cookie9Sided)
    }

    val sheetState = rememberStandardBottomSheetState(SheetValue.Expanded)
    val screenHeight = LocalWindowInfo.current.containerSize.height
    val currentSheetHeight = runCatching { screenHeight - sheetState.requireOffset() }
    val offset = currentSheetHeight.getOrDefault(0.0f).roundToInt()
    val offsetDp = with (LocalDensity.current) { offset.toDp() } - TopAppBarDefaults.TopAppBarExpandedHeight
    BottomSheetScaffold(
        sheetContent = {
            LazyRow(
                Modifier
                    .fillMaxWidth()
                    .windowInsetsPadding(WindowInsets.safeGestures.only(WindowInsetsSides.Bottom))
                    .padding(horizontal = 8.dp),
                horizontalArrangement = Arrangement.spacedBy((-4).dp)
            ) {
                itemsIndexed(model.guilds) { idx, guild ->
                    if (idx == 0) return@itemsIndexed
                    GuildButton(
                        model = guild,
                        morph = morph,
                        selected = model.selected.first == guild,
                        onSelected = { onNavigateGuild(guild) }
                    )
                }
            }
        },
        scaffoldState = rememberBottomSheetScaffoldState(
            bottomSheetState = sheetState
        ),
        topBar = {
            TopAppBar(
                title = { Text(model.guildName) }
            )
        },
    ) { innerPadding ->
        Channels(
            model = model,
            bottomSpace = offsetDp,
            padding = innerPadding,
            onNavigate = { onNavigateChannel(it) }
        )
    }
}

@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun SidebarNavigator(
    model: NavigatorModel,
    onNavigateGuild: (NavigatorModel.Guild) -> Unit,
    onNavigateChannel: (NavigatorModel.Channel) -> Unit,
) {
    val morph = remember {
        Morph(MaterialShapes.Diamond, MaterialShapes.Cookie9Sided)
    }
    Row(Modifier.fillMaxSize()) {
        LazyColumn(
            Modifier
                .background(MaterialTheme.colorScheme.surfaceContainerLow)
                .padding(horizontal = 8.dp)
                .fillMaxHeight(),
            contentPadding = WindowInsets.safeGestures.only(
                WindowInsetsSides.Top + WindowInsetsSides.Bottom
            ).asPaddingValues(),
            verticalArrangement = Arrangement.spacedBy((-4).dp)
        ) {
            itemsIndexed(model.guilds) { idx, guild ->
                GuildButton(
                    model = guild,
                    morph = morph,
                    selected = model.selected.first == guild,
                    onSelected = { onNavigateGuild(guild) }
                )
            }
        }
        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text(model.guildName) }
                )
            },
        ) { innerPadding ->
            Channels(model, 0.dp, innerPadding, onNavigate = { onNavigateChannel(it) })
        }
    }
}
