package com.pixelized.biblib.ui.screen.home

import android.content.res.Configuration
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.SnackbarResult
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.pullrefresh.PullRefreshState
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import coil.compose.AsyncImage
import com.pixelized.biblib.R
import com.pixelized.biblib.model.search.FilterType
import com.pixelized.biblib.model.search.SortType
import com.pixelized.biblib.model.search.SortValue
import com.pixelized.biblib.ui.LocalSnackHostState
import com.pixelized.biblib.ui.composable.Loader
import com.pixelized.biblib.ui.composable.colorElevation
import com.pixelized.biblib.ui.composable.scaffold.DetailBottomSheet
import com.pixelized.biblib.ui.composable.scaffold.DetailBottomSheetState
import com.pixelized.biblib.ui.composable.scaffold.FilterBottomSheet
import com.pixelized.biblib.ui.composable.scaffold.FilterBottomSheetResult
import com.pixelized.biblib.ui.composable.scaffold.FilterBottomSheetState
import com.pixelized.biblib.ui.composable.scaffold.SortBottomSheet
import com.pixelized.biblib.ui.composable.scaffold.SortBottomSheetResult
import com.pixelized.biblib.ui.composable.scaffold.SortBottomSheetState
import com.pixelized.biblib.ui.composable.scaffold.rememberDetailBottomSheetState
import com.pixelized.biblib.ui.composable.scaffold.rememberFilterBottomSheetState
import com.pixelized.biblib.ui.composable.scaffold.rememberSortBottomSheetState
import com.pixelized.biblib.ui.navigation.LocalScreenNavHostController
import com.pixelized.biblib.ui.navigation.navigateToProfile
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
import com.pixelized.biblib.ui.screen.home.common.item.LargeBookThumbnail
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnail
import com.pixelized.biblib.ui.screen.home.common.item.SmallBookThumbnail
import com.pixelized.biblib.ui.screen.home.common.preview.thumbnailPreviewResources
import com.pixelized.biblib.ui.screen.home.filter.FilterChip
import com.pixelized.biblib.ui.screen.home.filter.FilterChipUio
import com.pixelized.biblib.ui.screen.home.filter.filterPreview
import com.pixelized.biblib.ui.screen.home.header.LazyGridCollapsingHeaderLayout
import com.pixelized.biblib.ui.screen.home.options.Options
import com.pixelized.biblib.ui.screen.home.options.OptionsUio
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.imeHeight
import com.pixelized.biblib.utils.extention.navigationBarsHeight
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch

@Stable
@Immutable
sealed class HomeScreenErrorUio(
    @StringRes val message: Int,
    @StringRes val action: Int,
) {
    @Stable
    @Immutable
    class LibraryUpdate(
        @StringRes message: Int,
        @StringRes action: Int,
    ) : HomeScreenErrorUio(
        message = message,
        action = action,
    )
}

@Stable
@Immutable
enum class HomeDisplay {
    MICRO,
    SMALL,
    GRID,
}

@OptIn(ExperimentalMaterialApi::class, ExperimentalComposeUiApi::class)
@Composable
fun HomeScreen(
    bookViewModel: BookSearchViewModel = hiltViewModel(),
    profileViewModel: HomeViewModel = hiltViewModel(),
) {
    val context = LocalContext.current
    val navigation = LocalScreenNavHostController.current
    val snackBarHost = LocalSnackHostState.current
    val focus = LocalFocusManager.current
    val keyboard = LocalSoftwareKeyboardController.current
    val detailState: DetailBottomSheetState = rememberDetailBottomSheetState()
    val filterState: FilterBottomSheetState = rememberFilterBottomSheetState()
    val sortingState: SortBottomSheetState = rememberSortBottomSheetState()
    val scope = rememberCoroutineScope()
    val largeGridState = rememberLazyGridState()
    val smallListState = rememberLazyListState()
    val microListState = rememberLazyListState()
    val refreshState = rememberPullRefreshState(viewModel = bookViewModel)

    FilterBottomSheet(
        sheetState = filterState,
    ) {
        SortBottomSheet(
            sheetState = sortingState,
        ) {
            DetailBottomSheet(
                bottomDetailState = detailState,
                onAuthor = { label: String?, id: Int? ->
                    bookViewModel.source(clean = true) {
                        filterByAuthor(author = label, authorId = id)
                        sortBy(type = SortType.Title, value = SortValue.ASC)
                    }
                    scope.launch { detailState.collapse() }
                },
                onSeries = { label: String?, id: Int? ->
                    bookViewModel.source(clean = true) {
                        filterBySeries(series = label, seriesId = id)
                        sortBy(type = SortType.Series, value = SortValue.ASC)
                    }
                    scope.launch { detailState.collapse() }
                },
            ) {
                HomeScreenContent(
                    modifier = Modifier
                        .fillMaxSize()
                        .statusBarsPadding(),
                    avatar = profileViewModel.avatar,
                    search = bookViewModel.search,
                    onSearch = {
                        bookViewModel.filterByTitle(criteria = it)
                    },
                    onAvatarTap = {
                        navigation.navigateToProfile()
                    },
                    filters = bookViewModel.filters,
                    onFilter = rememberOnFilter(
                        filterViewModel = bookViewModel,
                        filterState = filterState,
                    ),
                    options = bookViewModel.options,
                    onUp = {
                        scope.launch {
                            launch { largeGridState.animateScrollToItem(index = 0) }
                            launch { smallListState.animateScrollToItem(index = 0) }
                            launch { microListState.animateScrollToItem(index = 0) }
                        }
                    },
                    onSort = {
                        // close the keyboard.
                        focus.clearFocus(force = true)
                        keyboard?.hide()
                        // open the sorting bottom sheet.
                        scope.launch {
                            val result = sortingState.show(sortBy = bookViewModel.sortBy)
                            if (result is SortBottomSheetResult.ActionPerformed) {
                                bookViewModel.source {
                                    sortBy(sortBy = result.sortBy)
                                }
                            }
                        }
                    },
                    onDisplay = bookViewModel::onDisplay,
                    isLoading = bookViewModel.updating,
                    refreshState = refreshState,
                    display = bookViewModel.display,
                    gridState = largeGridState,
                    items = bookViewModel.paging,
                    onBook = {
                        // close the keyboard.
                        focus.clearFocus(force = true)
                        keyboard?.hide()
                        // open the detail bottom sheet.
                        scope.launch {
                            detailState.expandBookDetail(id = it)
                        }
                    },
                )
            }
        }
    }

    CollapseKeyboardOnScrollHandler(
        largeGridState = largeGridState,
        smallListState = smallListState,
        microListState = microListState
    )

    LaunchedEffect(key1 = "HomeScreenLaunchedEffect") {
        // update
        launch {
            bookViewModel.updateLibrary()
        }
        // error management.
        launch {
            bookViewModel.error.collect {
                val result = snackBarHost.showSnackbar(
                    message = context.getString(it.message),
                    actionLabel = context.getString(it.action),
                )
                if (result == SnackbarResult.ActionPerformed) {
                    bookViewModel.updateLibrary()
                }
            }
        }
    }
}

@Composable
private fun rememberOnFilter(
    filterViewModel: BookSearchViewModel,
    filterState: FilterBottomSheetState,
): (FilterChipUio) -> Unit {
    val scope = rememberCoroutineScope()

    return remember {
        {
            if (it.id == FilterType.New) {
                filterViewModel.source {
                    filterByNew(isNew = it.selected.not())
                }
            } else {
                if (it.selected.not()) {
                    scope.launch {
                        val result = filterState.show(type = it.id)
                        if (result is FilterBottomSheetResult.ActionPerformed) {
                            filterViewModel.source {
                                when (it.id) {
                                    FilterType.Author -> filterByAuthor(
                                        author = result.filter.filterLabel,
                                        authorId = result.filter.filterId,
                                    )

                                    FilterType.Series -> filterBySeries(
                                        series = result.filter.filterLabel,
                                        seriesId = result.filter.filterId,
                                    )

                                    FilterType.Genre -> filterByGenre(
                                        genre = result.filter.filterLabel,
                                        genreId = result.filter.filterId,
                                    )

                                    FilterType.Language -> filterByLanguage(
                                        language = result.filter.filterLabel,
                                        languageId = result.filter.filterId,
                                    )

                                    else -> Unit
                                }
                            }
                        }
                    }
                } else {
                    filterViewModel.source {
                        when (it.id) {
                            FilterType.Author -> filterByAuthor(
                                author = null,
                                authorId = null,
                            )

                            FilterType.Series -> filterBySeries(
                                series = null,
                                seriesId = null,
                            )

                            FilterType.Genre -> filterByGenre(
                                genre = null,
                                genreId = null,
                            )

                            FilterType.Language -> filterByLanguage(
                                language = null,
                                languageId = null,
                            )

                            else -> Unit
                        }
                    }
                }
            }
        }
    }
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun HomeScreenContent(
    modifier: Modifier = Modifier,
    avatar: String? = null,
    search: State<String?>,
    onSearch: (String) -> Unit,
    onAvatarTap: () -> Unit,
    filters: List<State<FilterChipUio>>,
    onFilter: (filter: FilterChipUio) -> Unit,
    options: State<OptionsUio>,
    onUp: () -> Unit,
    onSort: () -> Unit,
    onDisplay: (HomeDisplay) -> Unit,
    isLoading: State<Boolean>,
    refreshState: PullRefreshState,
    display: State<HomeDisplay>,
    gridState: LazyGridState,
    items: Flow<PagingData<BookThumbnailUio>>,
    onBook: (id: Int) -> Unit,
) {
    val theme = MaterialTheme.bibLib

    HomeScreenLayout(
        modifier = modifier,
        toolbar = {
            HomeToolbar(
                modifier = Modifier
                    .padding(vertical = 8.dp)
                    .padding(horizontal = 16.dp),
                search = search,
                onSearch = onSearch,
                avatar = avatar,
                onAvatarTap = onAvatarTap,
            )
        },
        filter = {
            LazyRow(
                contentPadding = PaddingValues(horizontal = theme.dimen.thumbnail.padding),
                horizontalArrangement = Arrangement.spacedBy(8.dp),
            ) {
                items(items = filters) {
                    FilterChip(
                        modifier = Modifier
                            .height(32.dp)
                            .animateContentSize(),
                        filter = it,
                        onClick = { onFilter(it.value) },
                    )
                }
            }
        },
        options = {
            Options(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(32.dp)
                    .padding(horizontal = theme.dimen.thumbnail.padding),
                options = options.value,
                onUp = onUp,
                onSort = onSort,
                onMicro = { onDisplay(HomeDisplay.MICRO) },
                onSmall = { onDisplay(HomeDisplay.SMALL) },
                onLarge = { onDisplay(HomeDisplay.GRID) },
            )
        },
        loader = {
            Loader(
                modifier = Modifier
                    .padding(horizontal = 16.dp)
                    .height(3.dp),
                refreshState = refreshState,
                isLoading = isLoading,
            )
        },
        content = {
            val navigationBarsHeight = navigationBarsHeight()
            val imeHeight = imeHeight()
            val paddingValues = remember(navigationBarsHeight, imeHeight) {
                PaddingValues(
                    start = theme.dimen.thumbnail.padding,
                    end = theme.dimen.thumbnail.padding,
                    top = theme.dimen.thumbnail.padding,
                    bottom = theme.dimen.thumbnail.padding + max(navigationBarsHeight, imeHeight)
                )
            }
            Box(
                modifier = Modifier.pullRefresh(refreshState),
            ) {
                BookList(
                    modifier = Modifier.fillMaxSize(),
                    display = display,
                    items = items,
                    paddingValues = paddingValues,
                    state = gridState,
                    onBook = onBook,
                )
            }
        }
    )
}

@Composable
private fun HomeScreenLayout(
    modifier: Modifier = Modifier,
    toolbar: @Composable () -> Unit,
    filter: @Composable () -> Unit,
    options: @Composable () -> Unit,
    loader: @Composable () -> Unit,
    content: @Composable () -> Unit,
) {
    Surface(
        modifier = modifier,
        color = MaterialTheme.bibLib.colors.base.background,
    ) {
        Column(
            modifier = Modifier.fillMaxSize()
        ) {
            toolbar()

            LazyGridCollapsingHeaderLayout(
                header = {
                    Column(
                        modifier = Modifier.padding(top = 8.dp),
                        verticalArrangement = Arrangement.spacedBy(
                            space = MaterialTheme.bibLib.dimen.thumbnail.padding,
                        )
                    ) {
                        filter()
                        options()
                        loader()
                    }
                },
                lazyGrid = content,
            )
        }
    }
}

@Composable
private fun HomeToolbar(
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = remember { PaddingValues(start = 16.dp, end = 8.dp) },
    search: State<String?>,
    onSearch: (String) -> Unit,
    avatar: String? = null,
    onAvatarTap: () -> Unit,
) {
    val direction = LocalLayoutDirection.current
    Surface(
        modifier = modifier,
        shape = CircleShape,
        color = colorElevation(elevation = 1.dp),
    ) {
        Row(
            modifier = Modifier.padding(
                start = contentPadding.calculateStartPadding(direction),
                bottom = contentPadding.calculateBottomPadding(),
                end = contentPadding.calculateEndPadding(direction),
                top = contentPadding.calculateTopPadding(),
            ),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Icon(
                imageVector = Icons.Default.Search,
                tint = MaterialTheme.colors.onSurface,
                contentDescription = null
            )

            TextField(
                modifier = Modifier.weight(1f),
                label = {
                    Text(
                        color = MaterialTheme.bibLib.colors.typography.medium,
                        text = stringResource(id = R.string.search_title),
                    )
                },
                value = search.value ?: "",
                singleLine = true,
                colors = TextFieldDefaults.outlinedTextFieldColors(
                    focusedBorderColor = Color.Transparent,
                    unfocusedBorderColor = Color.Transparent,
                    disabledBorderColor = Color.Transparent,
                ),
                onValueChange = onSearch,
            )

            IconButton(
                onClick = onAvatarTap,
            ) {
                if (avatar != null) {
                    AsyncImage(
                        modifier = Modifier
                            .clip(CircleShape)
                            .size(MaterialTheme.bibLib.dimen.avatar.medium),
                        model = avatar,
                        placeholder = painterResource(id = R.drawable.ic_google),
                        contentScale = ContentScale.Fit,
                        contentDescription = null,
                    )
                } else {
                    Icon(
                        tint = MaterialTheme.colors.onSurface,
                        imageVector = Icons.Default.Person,
                        contentDescription = null,
                    )
                }
            }
        }
    }
}

@Composable
private fun BookList(
    modifier: Modifier = Modifier,
    display: State<HomeDisplay>,
    items: Flow<PagingData<BookThumbnailUio>>,
    paddingValues: PaddingValues,
    state: LazyGridState,
    onBook: (id: Int) -> Unit,
) {
    val data = items.collectAsLazyPagingItems()

    AnimatedVisibility(
        visible = display.value == HomeDisplay.MICRO,
        enter = fadeIn(tween(durationMillis = 150)),
        exit = fadeOut(tween())
    ) {
        LazyVerticalGrid(
            modifier = modifier,
            columns = GridCells.Fixed(1),
            contentPadding = paddingValues,
            horizontalArrangement = Arrangement.spacedBy(
                space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
            ),
            verticalArrangement = Arrangement.spacedBy(
                space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
            ),
            state = state,
        ) {
            items(
                count = data.itemCount,
                key = { index -> data[index]?.id ?: index }
            ) { index ->
                MicroBookThumbnail(
                    thumbnail = data[index],
                    onClick = { onBook(it.id) },
                )
            }
        }
    }

    AnimatedVisibility(
        visible = display.value == HomeDisplay.SMALL,
        enter = fadeIn(tween(durationMillis = 150)),
        exit = fadeOut(tween())
    ) {
        LazyVerticalGrid(
            modifier = modifier,
            columns = GridCells.Fixed(1),
            contentPadding = paddingValues,
            horizontalArrangement = Arrangement.spacedBy(
                space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
            ),
            verticalArrangement = Arrangement.spacedBy(
                space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
            ),
            state = state,
        ) {
            items(
                count = data.itemCount,
                key = { index -> data[index]?.id ?: index }
            ) { index ->
                SmallBookThumbnail(
                    thumbnail = data[index],
                    onClick = { onBook(it.id) },
                )
            }
        }
    }

    AnimatedVisibility(
        visible = display.value == HomeDisplay.GRID,
        enter = fadeIn(tween(durationMillis = 150)),
        exit = fadeOut(tween())
    ) {
        LazyVerticalGrid(
            modifier = modifier,
            columns = GridCells.Fixed(2),
            contentPadding = paddingValues,
            horizontalArrangement = Arrangement.spacedBy(
                space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
            ),
            verticalArrangement = Arrangement.spacedBy(
                space = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
            ),
            state = state,
        ) {
            items(
                count = data.itemCount,
                key = { index -> data[index]?.id ?: index }
            ) { index ->
                LargeBookThumbnail(
                    thumbnail = data[index],
                    onClick = { onBook(it.id) },
                )
            }
        }
    }
}

@Composable
@OptIn(ExperimentalMaterialApi::class)
private fun rememberPullRefreshState(
    viewModel: BookSearchViewModel,
    scope: CoroutineScope = rememberCoroutineScope()
): PullRefreshState {
    return rememberPullRefreshState(
        refreshing = viewModel.updating.value,
        onRefresh = {
            scope.launch {
                viewModel.updateLibrary()
            }
        },
    )
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun CollapseKeyboardOnScrollHandler(
    largeGridState: LazyGridState,
    smallListState: LazyListState,
    microListState: LazyListState,
) {
    val keyboard = LocalSoftwareKeyboardController.current
    val focus = LocalFocusManager.current

    if (
        largeGridState.isScrollInProgress
        || smallListState.isScrollInProgress
        || microListState.isScrollInProgress
    ) {
        focus.clearFocus()
        keyboard?.hide()
    }
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun HomeScreenPreview() {
    BibLibTheme {
        val options = remember {
            mutableStateOf(
                OptionsUio(sorting = false, micro = true, small = false, large = false)
            )
        }

        HomeScreenContent(
            search = remember { mutableStateOf("") },
            onSearch = { },
            avatar = null,
            onAvatarTap = { },
            filters = remember { filterPreview() },
            onFilter = { },
            options = options,
            onUp = { },
            onSort = { },
            onDisplay = { },
            isLoading = remember { mutableStateOf(false) },
            refreshState = rememberPullRefreshState(refreshing = false, onRefresh = { }),
            display = remember { mutableStateOf(HomeDisplay.MICRO) },
            gridState = rememberLazyGridState(),
            items = thumbnailPreviewResources(),
            onBook = { },
        )
    }
}