package hu.bbara.purefin.ui.screen.series.components

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.gestures.LocalBringIntoViewSpec
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.Spacer
import androidx.compose.foundation.layout.aspectRatio
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.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.PlayCircle
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
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 hu.bbara.purefin.core.image.ArtworkKind
import hu.bbara.purefin.core.image.ImageUrlBuilder
import hu.bbara.purefin.model.CastMember
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
import hu.bbara.purefin.ui.screen.home.components.TvHomeRowBringIntoViewSpec
import java.util.UUID
import androidx.tv.material3.Text as TvText

internal const val SeriesFirstSeasonTabTag = "series-first-season-tab"
internal const val SeriesNextUpEpisodeCardTag = "series-next-up-episode-card"

@Composable
internal fun TvSeriesMetaChips(series: Series) {
    MediaMetadataFlowRow(
        items = listOf(series.year, "${series.seasonCount} Seasons")
    )
}

@Composable
@OptIn(ExperimentalFoundationApi::class)
internal fun TvSeasonTabs(
    seasons: List<Season>,
    selectedSeason: Season?,
    modifier: Modifier = Modifier,
    selectedItemFocusRequester: FocusRequester? = null,
    firstItemTestTag: String? = null,
    requestSelectedItemFocus: Boolean = false,
    onSelectedItemFocusRequested: () -> Unit = {},
    onSelect: (Season) -> Unit
) {
    val selectedSeasonIndex = seasons.indexOf(selectedSeason).coerceAtLeast(0)
    val listState = rememberLazyListState()

    LaunchedEffect(selectedSeasonIndex, requestSelectedItemFocus, seasons.size) {
        if (seasons.isEmpty()) return@LaunchedEffect
        listState.scrollToItem(selectedSeasonIndex)
        if (requestSelectedItemFocus && selectedItemFocusRequester != null) {
            withFrameNanos { }
            selectedItemFocusRequester.requestFocus()
            onSelectedItemFocusRequested()
        }
    }

    LazyRow(
        state = listState,
        modifier = modifier
            .then(
                if (selectedItemFocusRequester != null) {
                    Modifier.focusRestorer(selectedItemFocusRequester)
                } else {
                    Modifier.focusRestorer()
                }
            )
            .focusGroup()
            .fillMaxWidth(),
        horizontalArrangement = Arrangement.spacedBy(0.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        itemsIndexed(seasons, key = { _, season -> season.id }) { index, season ->
            TvSeasonTab(
                name = season.name,
                isSelected = index == selectedSeasonIndex,
                onFocused = { onSelect(season) },
                onClick = { onSelect(season) },
                modifier = Modifier
                    .then(
                        if (index == selectedSeasonIndex && selectedItemFocusRequester != null) {
                            Modifier.focusRequester(selectedItemFocusRequester)
                        } else {
                            Modifier
                        }
                    )
                    .then(
                        if (index == 0 && firstItemTestTag != null) {
                            Modifier.testTag(firstItemTestTag)
                        } else {
                            Modifier
                        }
                    )
            )
        }
    }
}

@Composable
private fun TvSeasonTab(
    name: String,
    isSelected: Boolean,
    onFocused: () -> Unit,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val scheme = MaterialTheme.colorScheme
    val color = if (isSelected) scheme.primary else scheme.onSurface
    val isFocused = remember { mutableStateOf(false) }

    Box(
        modifier = modifier
            .onFocusChanged { state ->
                if (state.isFocused) {
                    onFocused()
                    isFocused.value = true
                } else {
                    isFocused.value = false
                }
            }
            .clickable(onClick = onClick)
            .borderIfFocused(isFocused.value, MaterialTheme.colorScheme.primary)
    ) {
        TvText(
            text = name,
            color = color,
            fontSize = 13.sp,
            fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
            modifier = Modifier
                .padding(horizontal = 16.dp, vertical = 8.dp)
        )
    }
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun TvEpisodeCarousel(
    episodes: List<Episode>,
    onPlayEpisode: (Episode) -> Unit,
    modifier: Modifier = Modifier,
    focusedEpisodeId: UUID? = null,
    requestFocus: Boolean = false,
    onFocusRequested: () -> Unit = {},
    upFocusRequester: FocusRequester? = null
) {
    val listState = rememberLazyListState()
    val rowScopedFocusRequester = remember { FocusRequester() }
    var rowFocusedEpisodeId by remember { mutableStateOf<UUID?>(null) }
    var requestedFocusEpisodeId by remember { mutableStateOf<UUID?>(null) }
    val focusedEpisodeIndex = focusedEpisodeId?.let { id ->
        episodes.indexOfFirst { it.id == id }
    } ?: -1
    val firstUnwatchedIndex = episodes.indexOfFirst { !it.watched }
    val targetEpisodeIndex = when {
        focusedEpisodeIndex >= 0 -> focusedEpisodeIndex
        firstUnwatchedIndex >= 0 -> firstUnwatchedIndex
        episodes.isNotEmpty() -> 0
        else -> -1
    }
    val targetEpisodeId = episodes.getOrNull(targetEpisodeIndex)?.id

    LaunchedEffect(episodes, targetEpisodeId) {
        if (episodes.none { it.id == rowFocusedEpisodeId }) {
            rowFocusedEpisodeId = targetEpisodeId
        }

        if (targetEpisodeIndex > 0) {
            listState.scrollToItem(targetEpisodeIndex)
        } else {
            listState.scrollToItem(0)
        }
    }

    LaunchedEffect(requestFocus, targetEpisodeId) {
        if (requestFocus && targetEpisodeId != null && requestedFocusEpisodeId != targetEpisodeId) {
            rowFocusedEpisodeId = targetEpisodeId
            if (targetEpisodeIndex > 0) {
                listState.scrollToItem(targetEpisodeIndex)
            } else {
                listState.scrollToItem(0)
            }
            withFrameNanos { }
            rowScopedFocusRequester.requestFocus()
            requestedFocusEpisodeId = targetEpisodeId
            onFocusRequested()
        }
    }

    CompositionLocalProvider(LocalBringIntoViewSpec provides TvHomeRowBringIntoViewSpec) {
        LazyRow(
            state = listState,
            modifier = modifier
                .focusRestorer(rowScopedFocusRequester)
                .focusGroup(),
            contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
            horizontalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            items(episodes, key = { episode -> episode.id }) { episode ->
                TvEpisodeCard(
                    episode = episode,
                    onPlayEpisode = { onPlayEpisode(episode) },
                    onFocused = { rowFocusedEpisodeId = episode.id },
                    modifier = Modifier
                        .then(
                            if (episode.id == rowFocusedEpisodeId) {
                                Modifier.focusRequester(rowScopedFocusRequester)
                            } else {
                                Modifier
                            }
                        )
                        .then(upFocusRequester?.let { requester ->
                            Modifier.focusProperties { up = requester }
                        } ?: Modifier)
                        .then(
                            if (episode.id == targetEpisodeId) {
                                Modifier.testTag(SeriesNextUpEpisodeCardTag)
                            } else {
                                Modifier
                            }
                        )
                )
            }
        }
    }
}

@Composable
internal fun TvSeriesHeroSection(
    series: Series,
    modifier: Modifier = Modifier
) {
    val scheme = MaterialTheme.colorScheme
    val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.82f)

    Column(
        modifier = modifier
            .padding(top = 24.dp)
            .fillMaxWidth()
    ) {
        Text(
            text = series.name,
            color = scheme.onBackground,
            fontSize = 24.sp,
            fontWeight = FontWeight.Bold,
            lineHeight = 28.sp,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis
        )
        Spacer(modifier = Modifier.height(4.dp))
        TvSeriesMetaChips(series = series)
        Spacer(modifier = Modifier.height(10.dp))
        Text(
            text = "Overview",
            color = scheme.onBackground,
            fontSize = 13.sp,
            fontWeight = FontWeight.Bold
        )
        Spacer(modifier = Modifier.height(2.dp))
        Text(
            text = series.synopsis,
            color = mutedStrong,
            fontSize = 12.sp,
            lineHeight = 16.sp,
            maxLines = 3,
            overflow = TextOverflow.Ellipsis,
            modifier = Modifier.fillMaxWidth(0.4f)
        )
    }
}

@Composable
private fun TvEpisodeCard(
    episode: Episode,
    onPlayEpisode: () -> Unit,
    onFocused: () -> Unit,
    modifier: Modifier = Modifier
) {
    val scheme = MaterialTheme.colorScheme
    val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
    var isFocused by remember { mutableStateOf(false) }
    val scale by animateFloatAsState(targetValue = if (isFocused) 1.07f else 1.0f, label = "scale")

    Column(
        modifier = modifier
            .width(260.dp)
            .graphicsLayer { scaleX = scale; scaleY = scale }
            .onFocusChanged {
                isFocused = it.isFocused
                if (it.isFocused) onFocused()
            }
            .clickable { onPlayEpisode() },
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .aspectRatio(16f / 9f)
                .clip(RoundedCornerShape(12.dp))
                .background(scheme.surface)
                .border(
                    width = if (isFocused) 2.dp else 1.dp,
                    color = if (isFocused) scheme.primary else scheme.outlineVariant,
                    shape = RoundedCornerShape(12.dp)
                )
        ) {
            PurefinAsyncImage(
                model = ImageUrlBuilder.finishImageUrl(episode.imageUrlPrefix, ArtworkKind.PRIMARY),
                contentDescription = null,
                modifier = Modifier.fillMaxSize(),
                contentScale = ContentScale.Crop
            )
            Box(
                modifier = Modifier
                    .matchParentSize()
                    .background(scheme.background.copy(alpha = 0.2f))
            )
            Icon(
                imageVector = Icons.Outlined.PlayCircle,
                contentDescription = null,
                tint = scheme.onBackground,
                modifier = Modifier
                    .align(Alignment.Center)
                    .size(32.dp)
            )
            Box(
                modifier = Modifier
                    .align(Alignment.BottomEnd)
                    .padding(6.dp)
                    .background(scheme.background.copy(alpha = 0.8f), RoundedCornerShape(6.dp))
                    .padding(horizontal = 6.dp, vertical = 2.dp)
            ) {
                Text(
                    text = episode.runtime,
                    color = scheme.onBackground,
                    fontSize = 10.sp,
                    fontWeight = FontWeight.Bold
                )
            }
            if (episode.watched.not() && (episode.progress ?: 0.0) > 0) {
                MediaProgressBar(
                    progress = (episode.progress ?: 0.0).toFloat().div(100),
                    modifier = Modifier
                        .align(Alignment.BottomStart)
                )
            } else {
                WatchStateBadge(
                    watched = episode.watched,
                    modifier = Modifier
                        .align(Alignment.TopEnd)
                        .padding(8.dp)
                )
            }
        }
        Column(
        ) {
            Text(
                text = episode.title,
                color = scheme.onBackground,
                fontSize = 14.sp,
                fontWeight = FontWeight.Bold,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
            Text(
                text = "Episode ${episode.index}",
                color = mutedStrong,
                fontSize = 12.sp,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}

@Composable
internal fun CastRow(cast: List<CastMember>, modifier: Modifier = Modifier) {
//    MediaCastRow(
//        cast = cast,
//        modifier = modifier,
//        cardWidth = 84.dp,
//        nameSize = 11.sp,
//        roleSize = 10.sp
//    )
}

private fun Modifier.borderIfFocused(isFocused: Boolean, borderColor: Color): Modifier =
    if (isFocused) {
        this.border(
            width = 2.dp,
            color = borderColor,
            shape = RoundedCornerShape(12.dp)
        )
    } else {
        this
    }

