package com.fabledsword.minstrel.player.ui

import androidx.compose.animation.Crossfade
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Music
import com.composables.icons.lucide.Pause
import com.composables.icons.lucide.Play
import com.composables.icons.lucide.SkipBack
import com.composables.icons.lucide.SkipForward
import com.fabledsword.minstrel.models.TrackRef
import com.fabledsword.minstrel.nav.HERO_KEY_NOW_PLAYING_COVER
import com.fabledsword.minstrel.nav.LocalAnimatedContentScope
import com.fabledsword.minstrel.nav.LocalSharedTransitionScope
import com.fabledsword.minstrel.shared.widgets.LikeButton
import com.fabledsword.minstrel.shared.widgets.ServerImage
import com.fabledsword.minstrel.shared.widgets.trackactions.TrackActionsViewModel
import com.fabledsword.minstrel.theme.FabledSwordFlatTokens

private const val MINI_BAR_HEIGHT_DP = 80
private const val MINI_BAR_TONAL_ELEVATION_DP = 4
private const val COVER_SIZE_DP = 48
private const val SCRUBBER_ROW_HEIGHT_DP = 4

// Upward flick speed (dp/s) that expands the bar into NowPlaying.
// Mirrors player_bar.dart's 200 px/s threshold; expressed in dp and
// converted via density so the gesture feels the same across screens.
private const val SWIPE_UP_VELOCITY_DP = 200

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
private fun MiniCover(coverUrl: String, contentDescription: String) {
    val sharedScope = LocalSharedTransitionScope.current
    val animatedScope = LocalAnimatedContentScope.current
    val heroModifier = if (sharedScope != null && animatedScope != null) {
        with(sharedScope) {
            Modifier.sharedElement(
                sharedContentState = rememberSharedContentState(
                    key = HERO_KEY_NOW_PLAYING_COVER,
                ),
                animatedVisibilityScope = animatedScope,
            )
        }
    } else {
        Modifier
    }
    Box(
        modifier = heroModifier
            .size(COVER_SIZE_DP.dp)
            .clip(RoundedCornerShape(FabledSwordFlatTokens.radiusSm))
            .background(MaterialTheme.colorScheme.surfaceVariant),
        contentAlignment = Alignment.Center,
    ) {
        // Cross-fade the artwork when the track changes so the cover
        // dissolves into the next instead of hard-swapping.
        Crossfade(targetState = coverUrl, label = "mini-cover") { url ->
            ServerImage(
                url = url,
                contentDescription = contentDescription,
                modifier = Modifier.size(COVER_SIZE_DP.dp),
            ) {
                Icon(
                    imageVector = Lucide.Music,
                    contentDescription = null,
                    tint = MaterialTheme.colorScheme.onSurfaceVariant,
                )
            }
        }
    }
}

/**
 * Collapsed player bar shown above the bottom nav. Returns nothing
 * when no track is loaded so it doesn't take screen space on a
 * fresh install. Tapping the body navigates to the full
 * NowPlayingScreen via [onExpandClick].
 *
 * Layout (Column):
 *   - Slim seek slider at the top (4dp track)
 *   - Row: cover | title/artist column | like | prev | play/pause | next
 *
 * No kebab on the mini bar (operator 2026-06-01): the full kebab
 * surface lives on NowPlayingScreen, and dropping it from the mini
 * gives the title/artist column the horizontal room it needs to
 * actually display the artist name without truncation.
 */
@Composable
fun MiniPlayer(
    onExpandClick: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: PlayerViewModel = hiltViewModel(),
    trackActionsViewModel: TrackActionsViewModel = hiltViewModel(),
) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    val track = state.currentTrack ?: return
    val isLiked by trackActionsViewModel.isLikedFlow(track.id)
        .collectAsStateWithLifecycle(initialValue = false)

    // Swipe up anywhere on the bar to expand into the full player —
    // mirrors player_bar.dart. We only act on a clear upward flick
    // (negative velocity past the threshold) so a slow tap-with-jitter
    // doesn't accidentally open the screen. The horizontal seek slider
    // keeps its own gestures; a vertical draggable only claims
    // vertical-dominant drags.
    val flickThresholdPx = with(LocalDensity.current) { SWIPE_UP_VELOCITY_DP.dp.toPx() }
    Surface(
        modifier = modifier
            .fillMaxWidth()
            .height(MINI_BAR_HEIGHT_DP.dp)
            .draggable(
                orientation = Orientation.Vertical,
                state = rememberDraggableState { },
                onDragStopped = { velocity -> if (velocity < -flickThresholdPx) onExpandClick() },
            ),
        color = MaterialTheme.colorScheme.surface,
        tonalElevation = MINI_BAR_TONAL_ELEVATION_DP.dp,
    ) {
        Column {
            // Non-interactive fill bar — no scrubber thumb. Tapping
            // the bar still expands to NowPlaying where scrubbing
            // lives. The displayed fraction interpolates per-frame
            // between PlayerController position ticks so the fill
            // glides instead of stepping every 500ms.
            val smoothPositionMs by rememberSmoothPositionMs(
                positionMs = state.positionMs,
                durationMs = state.durationMs,
                isPlaying = state.isPlaying,
            )
            MiniProgressFill(
                positionMs = smoothPositionMs,
                durationMs = state.durationMs,
            )
            MiniRow(
                track = track,
                isPlaying = state.isPlaying,
                isUpnpLoading = state.isUpnpLoading,
                isLiked = isLiked,
                onExpandClick = onExpandClick,
                onPlayPause = { if (state.isPlaying) viewModel.pause() else viewModel.play() },
                onPrev = viewModel::skipToPrevious,
                onNext = viewModel::skipToNext,
                onToggleLike = {
                    trackActionsViewModel.toggleLike(track.id, isLiked)
                },
            )
        }
    }
}

@Composable
private fun MiniProgressFill(positionMs: Long, durationMs: Long) {
    val fraction = if (durationMs > 0) {
        (positionMs.toFloat() / durationMs.toFloat()).coerceIn(0f, 1f)
    } else {
        0f
    }
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(SCRUBBER_ROW_HEIGHT_DP.dp)
            .background(MaterialTheme.colorScheme.surfaceVariant),
    ) {
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .fillMaxWidth(fraction)
                .background(MaterialTheme.colorScheme.primary),
        )
    }
}

@Composable
@Suppress("LongParameterList")
private fun MiniRow(
    track: TrackRef,
    isPlaying: Boolean,
    isUpnpLoading: Boolean,
    isLiked: Boolean,
    onExpandClick: () -> Unit,
    onPlayPause: () -> Unit,
    onPrev: () -> Unit,
    onNext: () -> Unit,
    onToggleLike: () -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        // Cover + title area expands to NowPlaying on tap, but the
        // transport buttons don't (each IconButton handles its own
        // clicks). Wrap only the cover-and-title region with the
        // clickable modifier rather than the whole row.
        Row(
            modifier = Modifier
                .weight(1f)
                .clickable(onClick = onExpandClick),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            MiniCover(coverUrl = track.coverUrl, contentDescription = track.title)
            Spacer(Modifier.width(12.dp))
            Column(modifier = Modifier.weight(1f)) {
                Text(
                    text = track.title,
                    style = MaterialTheme.typography.titleSmall,
                    color = MaterialTheme.colorScheme.onSurface,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
                Text(
                    text = track.artistName,
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
            }
        }
        LikeButton(liked = isLiked, onToggle = onToggleLike)
        TransportButton(icon = Lucide.SkipBack, description = "Previous", onClick = onPrev)
        MiniPlayPauseButton(
            isPlaying = isPlaying,
            isUpnpLoading = isUpnpLoading,
            onClick = onPlayPause,
        )
        TransportButton(icon = Lucide.SkipForward, description = "Next", onClick = onNext)
    }
}

@Composable
private fun MiniPlayPauseButton(
    isPlaying: Boolean,
    isUpnpLoading: Boolean,
    onClick: () -> Unit,
) {
    IconButton(onClick = onClick, enabled = !isUpnpLoading) {
        if (isUpnpLoading) {
            CircularProgressIndicator(
                modifier = Modifier.size(MINI_PLAY_PAUSE_SPINNER_DP.dp),
                strokeWidth = 2.dp,
            )
        } else {
            Icon(
                imageVector = if (isPlaying) Lucide.Pause else Lucide.Play,
                contentDescription = if (isPlaying) "Pause" else "Play",
                tint = MaterialTheme.colorScheme.onSurface,
            )
        }
    }
}

private const val MINI_PLAY_PAUSE_SPINNER_DP = 24

@Composable
private fun TransportButton(
    icon: androidx.compose.ui.graphics.vector.ImageVector,
    description: String,
    onClick: () -> Unit,
) {
    IconButton(onClick = onClick) {
        Icon(
            imageVector = icon,
            contentDescription = description,
            tint = MaterialTheme.colorScheme.onSurface,
        )
    }
}
