package com.pixelized.biblib.ui.composable.scaffold

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material.*
import androidx.compose.material.ModalBottomSheetValue.Hidden
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.model.search.FilterType
import com.pixelized.biblib.ui.screen.home.filter.FilterItemUio
import com.pixelized.biblib.ui.screen.home.filter.FilterPage
import com.pixelized.biblib.ui.screen.home.filter.IFilterViewModel
import com.pixelized.biblib.ui.screen.home.filter.viewModel.AuthorFilterViewModel
import com.pixelized.biblib.ui.screen.home.filter.viewModel.GenreFilterViewModel
import com.pixelized.biblib.ui.screen.home.filter.viewModel.LanguageFilterViewModel
import com.pixelized.biblib.ui.screen.home.filter.viewModel.SeriesFilterViewModel
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.statusBarsHeight
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.coroutines.resume

val LocalFilterBottomSheetState = staticCompositionLocalOf<FilterBottomSheetState> {
    error("LocalFilterBottomSheetState not yet ready")
}

@Stable
class FilterBottomSheetState(
    initialSheet: FilterType,
) {
    private val mutex = Mutex()

    var type: FilterType by mutableStateOf(value = initialSheet)
        private set

    var currentBottomSheetData by mutableStateOf<FilterBottomSheetData?>(value = null)
        private set

    suspend fun show(type: FilterType): FilterBottomSheetResult = mutex.withLock {
        try {
            return suspendCancellableCoroutine { continuation ->
                this.type = type
                currentBottomSheetData = FilterBottomSheetDataImpl(continuation)
            }
        } finally {
            currentBottomSheetData = null
        }
    }

    @Stable
    private class FilterBottomSheetDataImpl(
        private val continuation: CancellableContinuation<FilterBottomSheetResult>
    ) : FilterBottomSheetData {
        override fun performAction(filter: FilterItemUio) {
            if (continuation.isActive) continuation.resume(
                FilterBottomSheetResult.ActionPerformed(filter = filter),
            )
        }

        override fun dismiss() {
            if (continuation.isActive) continuation.resume(
                FilterBottomSheetResult.Dismissed,
            )
        }
    }
}

interface FilterBottomSheetData {
    fun performAction(filter: FilterItemUio)
    fun dismiss()
}

sealed class FilterBottomSheetResult {
    data class ActionPerformed(val filter: FilterItemUio) : FilterBottomSheetResult()
    object Dismissed : FilterBottomSheetResult()
}

@Composable
fun rememberFilterBottomSheetState(
    initialSheet: FilterType = FilterType.New
): FilterBottomSheetState = remember {
    FilterBottomSheetState(initialSheet = initialSheet)
}

@OptIn(ExperimentalMaterialApi::class, ExperimentalComposeUiApi::class)
@Composable
fun FilterBottomSheet(
    bottomSheetState: ModalBottomSheetState = rememberModalBottomSheetState(
        initialValue = Hidden,
        skipHalfExpanded = true,
    ),
    sheetState: FilterBottomSheetState = rememberFilterBottomSheetState(),
    sheetShape: Shape = MaterialTheme.bibLib.shapes.sheet,
    content: @Composable () -> Unit,
) {
    CompositionLocalProvider(
        LocalFilterBottomSheetState provides sheetState,
    ) {
        val keyboard = LocalSoftwareKeyboardController.current
        val focusManager: FocusManager = LocalFocusManager.current
        val currentBottomSheetData = sheetState.currentBottomSheetData

        // Check the state of the currentALOBottomSheetData and show / hide the bottomSheet accordingly.
        HandleBottomSheetData(
            bottomSheetData = currentBottomSheetData,
            shouldShow = {
                bottomSheetState.show()
            },
            shouldDismiss = {
                focusManager.clearFocus(force = true)
                keyboard?.hide()
                bottomSheetState.hide()
            }
        )
        // Check the state of the bottomSheetState and call for dismiss if needed.
        HandleBottomSheetDismiss(
            bottomSheetState = bottomSheetState,
            onDismiss = { currentBottomSheetData?.dismiss() },
        )
        // Handle back event.
        BackHandler(
            enabled = bottomSheetState.isVisible,
            onBack = { currentBottomSheetData?.dismiss() },
        )

        BoxWithConstraints {
            val statusBarHeight = statusBarsHeight()
            val bottomSheetHeight = remember(statusBarHeight) {
                maxHeight - statusBarHeight - 16.dp
            }

            ModalBottomSheetLayout(
                sheetState = bottomSheetState,
                sheetShape = sheetShape,
                content = content,
                scrimColor = Color.Transparent,
                sheetContent = {
                    val viewModel: IFilterViewModel? = when (sheetState.type) {
                        FilterType.Author -> hiltViewModel<AuthorFilterViewModel>()
                        FilterType.Series -> hiltViewModel<SeriesFilterViewModel>()
                        FilterType.Genre -> hiltViewModel<GenreFilterViewModel>()
                        FilterType.Language -> hiltViewModel<LanguageFilterViewModel>()
                        else -> null
                    }
                    if (viewModel != null) {
                        val focusRequester = remember { FocusRequester() }

                        FilterPage(
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(bottomSheetHeight),
                            viewModel = viewModel,
                            focusRequester = focusRequester,
                            onFilter = {
                                currentBottomSheetData?.performAction(filter = it)
                            },
                            onIMEDone = {
                                currentBottomSheetData?.dismiss()
                            }
                        )
                        LaunchedEffect(key1 = "FilterPageFocusRequest-${sheetState.type}") {
                            if (currentBottomSheetData != null) {
                                focusRequester.requestFocus()
                                keyboard?.show()
                            }
                        }
                    } else {
                        Box(modifier = Modifier.height(1.dp))
                    }
                },
            )
        }
    }
}

