@file:OptIn(androidx.compose.ui.ExperimentalComposeUiApi::class)

package com.ponzischeme89.memby.ui.genre

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.itemsIndexed as rowItemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.HelpOutline
import androidx.compose.material.icons.filled.AutoAwesome
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.Category
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Gavel
import androidx.compose.material.icons.filled.Landscape
import androidx.compose.material.icons.filled.LiveTv
import androidx.compose.material.icons.filled.LocalFireDepartment
import androidx.compose.material.icons.filled.MilitaryTech
import androidx.compose.material.icons.filled.MusicNote
import androidx.compose.material.icons.filled.SentimentVerySatisfied
import androidx.compose.material.icons.filled.SportsSoccer
import androidx.compose.material.icons.filled.TheaterComedy
import androidx.compose.material.icons.filled.VideoLibrary
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.material3.Icon
import androidx.tv.material3.Text
import com.ponzischeme89.memby.ServiceLocator
import com.ponzischeme89.memby.data.model.BaseItem
import com.ponzischeme89.memby.ui.FocusScaleContainer
import com.ponzischeme89.memby.ui.PosterGridCard
import com.ponzischeme89.memby.ui.theme.MembyAccent
import com.ponzischeme89.memby.ui.theme.MembyCardCorner
import com.ponzischeme89.memby.ui.theme.MembyMutedText
import com.ponzischeme89.memby.ui.theme.MembySurface
import com.ponzischeme89.memby.ui.theme.MembySurfaceRaised
import kotlinx.coroutines.flow.distinctUntilChanged

@Composable
fun GenreDiscoveryStrip(
    itemType: String,
    navigationFocusRequester: FocusRequester,
    entryFocusRequester: FocusRequester,
    onFocused: () -> Unit,
    onOpenCategory: (String) -> Unit,
    modifier: Modifier = Modifier,
) {
    val categories = remember(itemType) { genreCategoryTabs(itemType) }
    Column(modifier.padding(vertical = 4.dp)) {
        Text(
            "Browse genres",
            color = Color.White,
            fontSize = 20.sp,
            fontWeight = FontWeight.SemiBold,
            modifier = Modifier.padding(horizontal = 36.dp, vertical = 6.dp),
        )
        LazyRow(
            contentPadding = PaddingValues(horizontal = 36.dp, vertical = 6.dp),
            horizontalArrangement = Arrangement.spacedBy(12.dp),
        ) {
            rowItemsIndexed(categories, key = { _, category -> category.id }) { index, category ->
                GenreDiscoveryCard(
                    category = category,
                    onFocused = onFocused,
                    onClick = { onOpenCategory(category.id) },
                    modifier = Modifier
                        .then(if (index == 0) Modifier.focusRequester(entryFocusRequester) else Modifier)
                        .focusProperties { if (index == 0) left = navigationFocusRequester },
                )
            }
        }
    }
}

@Composable
private fun GenreDiscoveryCard(
    category: GenreCategory,
    onFocused: () -> Unit,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val visual = remember(category.icon) { genreVisual(category.icon) }
    FocusScaleContainer(
        onFocused = onFocused,
        onClick = onClick,
        contentDescription = "Browse ${category.label}",
        modifier = modifier.width(142.dp),
    ) { focused ->
        Column {
            Box(
                Modifier
                    .fillMaxWidth()
                    .aspectRatio(2f / 3f)
                    .background(visual.colour, RoundedCornerShape(MembyCardCorner))
                    .border(
                        2.dp,
                        if (focused) Color.White else Color.White.copy(alpha = 0.07f),
                        RoundedCornerShape(MembyCardCorner),
                    ),
                contentAlignment = Alignment.Center,
            ) {
                Icon(
                    visual.icon,
                    contentDescription = null,
                    tint = Color.White.copy(alpha = 0.92f),
                    modifier = Modifier.size(48.dp),
                )
            }
            Spacer(Modifier.height(8.dp))
            Text(
                category.label,
                color = if (focused) Color.White else MembyMutedText,
                fontSize = 14.sp,
                fontWeight = FontWeight.SemiBold,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
            )
        }
    }
}

private data class GenreVisual(val icon: ImageVector, val colour: Color)

private fun genreVisual(icon: GenreCategoryIcon): GenreVisual = when (icon) {
    GenreCategoryIcon.ALL -> GenreVisual(Icons.Default.Category, Color(0xFF4F46A5))
    GenreCategoryIcon.ACTION -> GenreVisual(Icons.Default.Bolt, Color(0xFFB45309))
    GenreCategoryIcon.COMEDY -> GenreVisual(Icons.Default.TheaterComedy, Color(0xFF15803D))
    GenreCategoryIcon.CRIME -> GenreVisual(Icons.Default.Gavel, Color(0xFF475569))
    GenreCategoryIcon.DRAMA -> GenreVisual(Icons.Default.TheaterComedy, Color(0xFF7E22CE))
    GenreCategoryIcon.HORROR -> GenreVisual(Icons.Default.LocalFireDepartment, Color(0xFF991B1B))
    GenreCategoryIcon.MYSTERY -> GenreVisual(Icons.AutoMirrored.Filled.HelpOutline, Color(0xFF4338CA))
    GenreCategoryIcon.SCI_FI -> GenreVisual(Icons.Default.AutoAwesome, Color(0xFF0369A1))
    GenreCategoryIcon.THRILLER -> GenreVisual(Icons.Default.VisibilityOff, Color(0xFF0F766E))
    GenreCategoryIcon.WAR -> GenreVisual(Icons.Default.MilitaryTech, Color(0xFF57534E))
    GenreCategoryIcon.FAMILY -> GenreVisual(Icons.Default.SentimentVerySatisfied, Color(0xFFDB2777))
    GenreCategoryIcon.DOCUMENTARY -> GenreVisual(Icons.Default.VideoLibrary, Color(0xFF0E7490))
    GenreCategoryIcon.ROMANCE -> GenreVisual(Icons.Default.Favorite, Color(0xFFBE185D))
    GenreCategoryIcon.WESTERN -> GenreVisual(Icons.Default.Landscape, Color(0xFF92400E))
    GenreCategoryIcon.MUSIC -> GenreVisual(Icons.Default.MusicNote, Color(0xFF6D28D9))
    GenreCategoryIcon.SPORT -> GenreVisual(Icons.Default.SportsSoccer, Color(0xFF047857))
    GenreCategoryIcon.REALITY -> GenreVisual(Icons.Default.LiveTv, Color(0xFFC2410C))
}

@Composable
fun GenreBrowseScreen(
    itemType: String,
    initialCategoryId: String,
    favouriteStates: Map<String, Boolean>,
    playedStates: Map<String, Boolean>,
    navigationFocusRequester: FocusRequester,
    contentFocusRequester: FocusRequester,
    returnFocusItemId: String?,
    returnFocusRequester: FocusRequester,
    onItemFocused: (BaseItem) -> Unit,
    onItemSelected: (BaseItem) -> Unit,
    onClose: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val mediaLabel = if (itemType.equals("Series", ignoreCase = true)) "TV shows" else "movies"
    val browseViewModel: GenreBrowseViewModel = viewModel(
        key = "genre-browse-${itemType.lowercase()}",
        factory = remember(itemType) {
            GenreBrowseViewModelFactory(ServiceLocator.repository, itemType)
        },
    )
    val state by browseViewModel.state.collectAsStateWithLifecycle()
    val gridState = rememberLazyGridState()
    val tabState = androidx.compose.foundation.lazy.rememberLazyListState()
    val selectedCategory = remember(state.selectedCategoryId, itemType) {
        genreCategory(itemType, state.selectedCategoryId)
    }

    LaunchedEffect(initialCategoryId) { browseViewModel.selectCategory(initialCategoryId) }
    LaunchedEffect(favouriteStates) { browseViewModel.applyFavouriteStates(favouriteStates) }
    LaunchedEffect(playedStates) { browseViewModel.applyPlayedStates(playedStates) }
    LaunchedEffect(state.selectedCategoryId) {
        val index = state.categories.indexOfFirst { it.id == state.selectedCategoryId }.coerceAtLeast(0)
        if (state.selectedCategoryId != null) {
            gridState.scrollToItem(0)
            tabState.scrollToItem(index)
        }
    }
    LaunchedEffect(Unit) {
        kotlinx.coroutines.delay(32L)
        runCatching { contentFocusRequester.requestFocus() }
    }
    BackHandler(onBack = onClose)

    BoxWithConstraints(modifier.fillMaxSize().background(MembySurface)) {
        val columns = when {
            maxWidth >= 1080.dp -> 7
            maxWidth >= 860.dp -> 6
            maxWidth >= 680.dp -> 5
            else -> 4
        }
        val spacing = 16.dp
        val horizontalPadding = 36.dp
        val cardWidth = ((maxWidth - horizontalPadding * 2 - spacing * (columns - 1)) / columns)
            .coerceAtLeast(112.dp)

        Column(Modifier.fillMaxSize().padding(top = 24.dp)) {
            Row(
                modifier = Modifier.padding(horizontal = horizontalPadding),
                verticalAlignment = Alignment.CenterVertically,
            ) {
                FocusScaleContainer(
                    onFocused = {},
                    onClick = onClose,
                    contentDescription = "Back to $mediaLabel",
                    modifier = Modifier
                        .size(42.dp)
                        .focusProperties { left = navigationFocusRequester }
                        .background(MembySurfaceRaised, RoundedCornerShape(10.dp)),
                ) { focused ->
                    Box(
                        Modifier
                            .fillMaxSize()
                            .background(if (focused) Color.White else Color.Transparent, RoundedCornerShape(10.dp)),
                        contentAlignment = Alignment.Center,
                    ) {
                        Icon(
                            Icons.AutoMirrored.Filled.ArrowBack,
                            contentDescription = null,
                            tint = if (focused) MembySurface else Color.White,
                            modifier = Modifier.size(22.dp),
                        )
                    }
                }
                Spacer(Modifier.width(14.dp))
                Column {
                    Text(
                        "Browse $mediaLabel",
                        color = Color.White,
                        fontSize = 26.sp,
                        fontWeight = FontWeight.SemiBold,
                    )
                    Text(
                        selectedCategory.label,
                        color = MembyMutedText,
                        fontSize = 14.sp,
                    )
                }
            }

            Spacer(Modifier.height(16.dp))
            LazyRow(
                state = tabState,
                contentPadding = PaddingValues(horizontal = horizontalPadding),
                horizontalArrangement = Arrangement.spacedBy(8.dp),
            ) {
                rowItemsIndexed(state.categories, key = { _, category -> category.id }) { index, category ->
                    val selected = category.id == state.selectedCategoryId
                    FocusScaleContainer(
                        onFocused = {},
                        onClick = {
                            if (!selected) {
                                browseViewModel.selectCategory(category.id)
                            }
                        },
                        contentDescription = "Browse ${category.label}",
                        modifier = Modifier
                            .then(if (selected) Modifier.focusRequester(contentFocusRequester) else Modifier)
                            .focusProperties { if (index == 0) left = navigationFocusRequester }
                            .background(
                                when {
                                    selected -> MembyAccent
                                    else -> MembySurfaceRaised.copy(alpha = 0.72f)
                                },
                                RoundedCornerShape(18.dp),
                            )
                            .border(
                                1.dp,
                                if (selected) Color.White.copy(alpha = 0.18f) else Color.White.copy(alpha = 0.06f),
                                RoundedCornerShape(18.dp),
                            ),
                    ) { focused ->
                        Text(
                            category.label,
                            color = when {
                                focused -> MembySurface
                                selected -> Color.White
                                else -> MembyMutedText
                            },
                            fontSize = 14.sp,
                            fontWeight = if (selected) FontWeight.Bold else FontWeight.SemiBold,
                            modifier = Modifier
                                .background(
                                    if (focused) Color.White else Color.Transparent,
                                    RoundedCornerShape(18.dp),
                                )
                                .padding(horizontal = 17.dp, vertical = 9.dp),
                        )
                    }
                }
            }
            Spacer(Modifier.height(18.dp))

            when {
                state.selectedCategoryId == null -> GenreMessage("Loading $mediaLabel…")
                state.isLoading && state.items.isEmpty() -> GenreMessage(
                    "Loading ${selectedCategory.label}…",
                )
                state.errorMessage != null && state.items.isEmpty() -> GenreRetry(
                    message = state.errorMessage.orEmpty(),
                    onRetry = browseViewModel::retry,
                )
                state.items.isEmpty() -> GenreMessage(
                    if (selectedCategory.genres.isEmpty()) "No $mediaLabel were found."
                    else "No ${selectedCategory.label.lowercase()} $mediaLabel were found.",
                )
                else -> {
                    LaunchedEffect(gridState, state.items.size, state.canLoadMore) {
                        snapshotFlow { gridState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: -1 }
                            .distinctUntilChanged()
                            .collect { last ->
                                if (state.canLoadMore && last >= state.items.size - columns * 2) {
                                    browseViewModel.loadMore()
                                }
                            }
                    }
                    LazyVerticalGrid(
                        columns = GridCells.Fixed(columns),
                        state = gridState,
                        contentPadding = PaddingValues(
                            start = horizontalPadding,
                            end = horizontalPadding,
                            bottom = 72.dp,
                        ),
                        horizontalArrangement = Arrangement.spacedBy(spacing),
                        verticalArrangement = Arrangement.spacedBy(20.dp),
                        modifier = Modifier.fillMaxSize(),
                    ) {
                        itemsIndexed(
                            state.items,
                            key = { _, item -> item.id },
                            contentType = { _, _ -> "genre-poster" },
                        ) { index, item ->
                            val favourite = favouriteStates[item.id]
                            val displayedItem = if (favourite == null || favourite == item.isFavorite) {
                                item
                            } else {
                                item.withFavourite(favourite)
                            }
                            PosterGridCard(
                                item = displayedItem,
                                width = cardWidth,
                                onFocused = { onItemFocused(displayedItem) },
                                onClick = { onItemSelected(displayedItem) },
                                onLongClick = { onItemSelected(displayedItem) },
                                modifier = Modifier
                                    .then(
                                        if (item.id == returnFocusItemId) {
                                            Modifier.focusRequester(returnFocusRequester)
                                        } else {
                                            Modifier
                                        },
                                    )
                                    .focusProperties {
                                        if (index % columns == 0) left = navigationFocusRequester
                                    },
                            )
                        }
                        if (state.isLoadingMore) {
                            item(span = { GridItemSpan(maxLineSpan) }) {
                                GenreMessage("Loading more…")
                            }
                        } else if (state.errorMessage != null) {
                            item(span = { GridItemSpan(maxLineSpan) }) {
                                GenreRetry(
                                    message = state.errorMessage.orEmpty(),
                                    onRetry = browseViewModel::retry,
                                )
                            }
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun GenreMessage(message: String) {
    Box(Modifier.fillMaxWidth().padding(36.dp), contentAlignment = Alignment.Center) {
        Text(message, color = MembyMutedText, fontSize = 15.sp)
    }
}

@Composable
private fun GenreRetry(message: String, onRetry: () -> Unit) {
    Column(
        Modifier.fillMaxWidth().padding(36.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        Text(message, color = MembyMutedText, fontSize = 15.sp, maxLines = 2, overflow = TextOverflow.Ellipsis)
        FocusScaleContainer(
            onFocused = {},
            onClick = onRetry,
            contentDescription = "Try loading the genre again",
            modifier = Modifier
                .background(MembyAccent, RoundedCornerShape(9.dp))
                .semantics { contentDescription = "Try again" },
        ) { focused ->
            Text(
                "Try again",
                color = Color.White,
                fontWeight = FontWeight.Bold,
                modifier = Modifier.padding(horizontal = 18.dp, vertical = 10.dp),
            )
        }
    }
}
