package com.pixelized.headache.ui.page.summary.yearly

import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.snapping.SnapPosition
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.keepScreenOn
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.pixelized.headache.ui.navigation.destination.navigateToEventPage
import com.pixelized.headache.ui.navigation.main.LocalMainNavigator
import com.pixelized.headache.ui.theme.HeadacheTheme
import com.pixelized.headache.ui.theme.headache
import com.pixelized.headache.utils.extention.calculate
import java.util.Calendar

@Stable
data object YearSummaryPageDefault {
    @Stable
    val paddingValues = PaddingValues(
        start = 16.dp,
        end = 16.dp,
        bottom = 16.dp + 16.dp + 56.dp,
    )

    @Stable
    val space = 8.dp

    @Stable
    val column = 3
}

@Stable
data class YearUio(
    val year: Int,
    val months: List<YearSummaryMonthUio>,
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun YearSummaryPage(
    viewModel: YearSummaryViewModel = hiltViewModel(),
) {
    val navigation = LocalMainNavigator.current
    val uio = viewModel.events.collectAsStateWithLifecycle()

    YearSummaryContent(
        modifier = Modifier
            .keepScreenOn()
            .fillMaxSize(),
        uio = uio,
        onMonth = {
            navigation.navigateToEventPage(date = it.date)
        },
    )
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun YearSummaryContent(
    modifier: Modifier = Modifier,
    state: LazyGridState = rememberLazyGridState(),
    flingBehavior: FlingBehavior = rememberSnapFlingBehavior(
        lazyGridState = state,
        snapPosition = SnapPosition.Start,
    ),
    paddingValues: PaddingValues = YearSummaryPageDefault.paddingValues,
    space: Dp = YearSummaryPageDefault.space,
    daySize: Dp = rememberDaySize(column = YearSummaryPageDefault.column),
    uio: State<List<YearUio>>,
    onMonth: (YearSummaryMonthUio) -> Unit,
) {
    val typography = MaterialTheme.typography
    val colorScheme = MaterialTheme.headache.colorScheme
    val defaultTextStyle = remember(typography, colorScheme) {
        typography.labelSmall.copy(color = colorScheme.base.onSurface)
    }
    val headacheTextStyle = remember(typography, colorScheme) {
        defaultTextStyle.copy(color = colorScheme.calendar.onHeadache)
    }
    LazyVerticalGrid(
        modifier = modifier,
        state = state,
        flingBehavior = flingBehavior,
        columns = GridCells.Adaptive(minSize = daySize * 7),
        horizontalArrangement = Arrangement.spacedBy(space = space),
        verticalArrangement = Arrangement.spacedBy(space = space),
        reverseLayout = true,
        contentPadding = paddingValues,
    ) {
        uio.value.forEachIndexed { index, (year, months) ->
            items(
                items = months,
                key = { it.date.time },
                contentType = { "Month" },
            ) {
                YearSummaryMonth(
                    uio = it,
                    defaultTextStyle = defaultTextStyle,
                    headacheTextStyle = headacheTextStyle,
                    daySize = daySize,
                    onMonth = onMonth,
                )
            }
            item(
                span = { GridItemSpan(maxLineSpan) },
                contentType = { "Title" },
            ) {
                Text(
                    modifier = Modifier.padding(top = 16.dp),
                    style = MaterialTheme.typography.displaySmall,
                    text = "$year",
                )
            }
        }
    }
}

@Composable
private fun rememberDaySize(
    column: Int,
    paddingValues: PaddingValues = YearSummaryPageDefault.paddingValues,
    space: Dp = YearSummaryPageDefault.space,
): Dp {
    val density = LocalDensity.current
    val windowInfo = LocalWindowInfo.current
    val screenWidth = remember(density, windowInfo) {
        with(density) { windowInfo.containerSize.width.toDp() }
    }
    val (start, _, end, _) = paddingValues.calculate()
    return remember {
        (screenWidth - space * (column - 1) - start - end) / (7 * column)
    }
}

@Composable
@Preview()
private fun YearSummaryPreview(
    @PreviewParameter(YearPreviewProvider::class) preview: List<YearUio>,
) {
    HeadacheTheme {
        Surface {
            YearSummaryContent(
                uio = remember { mutableStateOf(preview) },
                onMonth = { },
            )
        }
    }
}

private class YearPreviewProvider() : PreviewParameterProvider<List<YearUio>> {

    private fun year(
        year: Int,
        vararg months: YearSummaryMonthUio,
    ) = YearUio(
        year = year,
        months = months.toList(),
    )

    override val values: Sequence<List<YearUio>>
        get() = with(MonthPreviewProviderHelper) {
            sequenceOf(
                listOf(
                    year(
                        year = 2025,
                        month(
                            month = Calendar.JANUARY,
                            year = 2025,
                            week(mon = null, tue = null),
                            week(),
                            week(),
                            week(),
                            week(sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.FEBRUARY,
                            year = 2025,
                            week(mon = null, tue = null, wen = null, thu = null, fry = null),
                            week(),
                            week(),
                            week(),
                            week(sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.MARCH,
                            year = 2025,
                            week(mon = null, tue = null, wen = null, thu = null, fry = null),
                            week(),
                            week(),
                            week(),
                            week(),
                            week(
                                tue = null,
                                wen = null,
                                thu = null,
                                fry = null,
                                sat = null,
                                sun = null
                            ),
                        ),
                        month(
                            month = Calendar.APRIL,
                            year = 2025,
                            week(mon = null),
                            week(),
                            week(),
                            week(),
                            week(thu = null, fry = null, sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.MAY,
                            year = 2025,
                            week(mon = null, tue = null, wen = null),
                            week(),
                            week(),
                            week(),
                            week(sun = null),
                        ),
                        month(
                            month = Calendar.JUNE,
                            year = 2025,
                            week(
                                mon = null,
                                tue = null,
                                wen = null,
                                thu = null,
                                fry = null,
                                sat = null
                            ),
                            week(),
                            week(),
                            week(),
                            week(),
                            week(
                                tue = null,
                                wen = null,
                                thu = null,
                                fry = null,
                                sat = null,
                                sun = null
                            ),
                        ),
                        month(
                            month = Calendar.JULY,
                            year = 2025,
                            week(mon = null, sun = headache(spifen = true)),
                            week(wen = headache(spifen = true)),
                            week(sat = headache()),
                            week(
                                wen = headache(spifen = true),
                                thu = headache(),
                                sat = headache(spifen = true),
                            ),
                            week(wen = headache(), fry = null, sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.AUGUST,
                            year = 2025,
                            week(mon = null, tue = null, wen = null, thu = null),
                            week(wen = headache(), fry = headache()),
                            week(fry = headache(spifen = true)),
                            week(tue = headache(spifen = true), fry = headache()),
                            week(
                                tue = headache(spifen = true, eletriptan = true),
                                wen = headache(),
                                sat = headache(spifen = true),
                            ),
                        ),
                        month(
                            month = Calendar.SEPTEMBER,
                            year = 2025,
                            week(
                                tue = headache(spifen = true, eletriptan = true),
                                wen = headache(spifen = true),
                                fry = headache(),
                                sat = headache(),
                                sun = headache(),
                            ),
                            week(
                                sun = headache(spifen = true),
                            ),
                            week(
                                wen = headache(spifen = true),
                                tue = headache(),
                            ),
                            week(),
                            week(wen = null, thu = null, fry = null, sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.OCTOBER,
                            year = 2025,
                            week(mon = null, tue = null),
                            week(),
                            week(),
                            week(),
                            week(sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.NOVEMBER,
                            year = 2025,
                            week(mon = null, tue = null, wen = null, thu = null, fry = null),
                            week(),
                            week(),
                            week(),
                            week(),
                        ),
                        month(
                            month = Calendar.DECEMBER,
                            year = 2025,
                            week(),
                            week(),
                            week(),
                            week(),
                            week(thu = null, fry = null, sat = null, sun = null),
                        ),
                    ),
                    year(
                        year = 2024,
                        month(
                            month = Calendar.JANUARY,
                            year = 2024,
                            week(mon = null, tue = null),
                            week(),
                            week(),
                            week(),
                            week(sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.FEBRUARY,
                            year = 2024,
                            week(mon = null, tue = null, wen = null, thu = null, fry = null),
                            week(),
                            week(),
                            week(),
                            week(sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.MARCH,
                            year = 2024,
                            week(mon = null, tue = null, wen = null, thu = null, fry = null),
                            week(),
                            week(),
                            week(),
                            week(),
                            week(
                                tue = null,
                                wen = null,
                                thu = null,
                                fry = null,
                                sat = null,
                                sun = null
                            ),
                        ),
                        month(
                            month = Calendar.APRIL,
                            year = 2024,
                            week(mon = null),
                            week(),
                            week(),
                            week(),
                            week(thu = null, fry = null, sat = null, sun = null),
                        ),
                        month(
                            month = Calendar.MAY,
                            year = 2024,
                            week(mon = null, tue = null, wen = null),
                            week(),
                            week(),
                            week(),
                            week(sun = null),
                        ),
                        month(
                            month = Calendar.JUNE,
                            year = 2024,
                            week(
                                mon = null,
                                tue = null,
                                wen = null,
                                thu = null,
                                fry = null,
                                sat = null
                            ),
                            week(),
                            week(),
                            week(),
                            week(),
                            week(
                                tue = null,
                                wen = null,
                                thu = null,
                                fry = null,
                                sat = null,
                                sun = null
                            ),
                        ),
                    )
                ),
            )
        }
}