package ca.ksamad.encore.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
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.filled.Add
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import ca.ksamad.encore.mpd.model.MpdAlbum
import ca.ksamad.encore.mpd.model.MpdSong
import ca.ksamad.encore.playback.AlbumArt
import kotlinx.coroutines.launch

/**
 * One album, in detail: its cover, whole-album Play / Add-to-queue actions, and the track list.
 * Tapping Play replaces the queue and starts the album (leaving this screen); tapping a track does
 * the same but starts the album from that track onward. Swiping a track queues it (right) or plays
 * it next (left), with the same semantics as swiping an album in the library.
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AlbumDetailScreen(
    vm: PlayerViewModel,
    album: MpdAlbum,
    onBack: () -> Unit,
    onPlay: () -> Unit,
) {
    // null = still loading the track list.
    val tracks by
        produceState<List<MpdSong>?>(initialValue = null, album) {
            value = vm.loadAlbumTracks(album.name, album.albumArtist)
        }

    // The album's audio format, taken from the tracks we already loaded (MPD reports a per-song
    // `Format` in the track listing), so no extra network call. Null until tracks arrive.
    val albumFormat = remember(tracks) { tracks?.let(::representativeFormat) }

    val snackbarHostState = remember { SnackbarHostState() }
    val scope = rememberCoroutineScope()

    fun flash(message: String) {
        snackbarHostState.currentSnackbarData?.dismiss()
        scope.launch { snackbarHostState.showSnackbar(message) }
    }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(album.name, maxLines = 1, overflow = TextOverflow.Ellipsis) },
                navigationIcon = {
                    IconButton(onClick = onBack) {
                        Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
                    }
                },
            )
        },
        snackbarHost = { SnackbarHost(snackbarHostState) },
    ) { innerPadding ->
        LazyColumn(modifier = Modifier.fillMaxSize().padding(innerPadding)) {
            item {
                AlbumDetailHeader(
                    album = album,
                    format = albumFormat,
                    onPlay = {
                        vm.playAlbum(album.name, album.albumArtist)
                        onPlay()
                    },
                    onQueue = {
                        vm.queueAlbum(album.name, album.albumArtist)
                        flash("Added “${album.name}” to the queue")
                    },
                )
            }

            // One track row, wired to its tap + swipe actions. Reused whether the list
            // is flat or split into disc groups.
            val trackItem: @Composable (MpdSong) -> Unit = { track ->
                TrackRow(
                    track = track,
                    onPlay = {
                        // Play the album from this track on: replace the queue with the
                        // tapped track and everything after it (the list is already in
                        // disc/track order), then start playing. Stay on the album page —
                        // just flash a confirmation rather than jumping to now-playing.
                        val loaded = tracks ?: return@TrackRow
                        val fromHere = loaded.dropWhile { it.uri != track.uri }.map { it.uri }
                        vm.playTracks(fromHere)
                        flash("Playing “${track.title ?: track.uri}”")
                    },
                    onQueue = {
                        vm.queueTrack(track.uri)
                        flash("Added “${track.title ?: track.uri}” to the queue")
                    },
                    onPlayNext = {
                        vm.playTrackNext(track.uri)
                        flash("“${track.title ?: track.uri}” will play next")
                    },
                )
            }

            val loaded = tracks
            when {
                loaded == null -> {
                    item {
                        Box(
                            Modifier.fillMaxWidth().padding(32.dp),
                            contentAlignment = Alignment.Center,
                        ) {
                            CircularProgressIndicator()
                        }
                    }
                }

                loaded.isEmpty() -> {
                    item {
                        Box(
                            Modifier.fillMaxWidth().padding(32.dp),
                            contentAlignment = Alignment.Center,
                        ) {
                            Text(
                                "No tracks found",
                                color = MaterialTheme.colorScheme.onSurfaceVariant,
                            )
                        }
                    }
                }

                // Single disc (or untagged): a plain flat list.
                loaded.map(::discNumberOf).distinct().size <= 1 -> {
                    items(loaded, key = { it.uri }) { trackItem(it) }
                }

                // Multi-disc: a light "Disc N" header before each disc's tracks. The
                // list arrives already sorted by (disc, track), so groupBy keeps order.
                else -> {
                    loaded.groupBy(::discNumberOf).forEach { (disc, discTracks) ->
                        item(key = "disc-$disc") { DiscHeader(disc) }
                        items(discTracks, key = { it.uri }) { trackItem(it) }
                    }
                }
            }
        }
    }
}

/** Cover, title/artist, audio-format pills, and the two whole-album action buttons. */
@Composable
private fun AlbumDetailHeader(
    album: MpdAlbum,
    format: String?,
    onPlay: () -> Unit,
    onQueue: () -> Unit,
) {
    Column(
        modifier = Modifier.fillMaxWidth().padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        ArtImage(
            model = AlbumArt(album.name, album.albumArtist),
            iconSize = 72.dp,
            modifier = Modifier.fillMaxWidth(0.85f).aspectRatio(1f).clip(RoundedCornerShape(12.dp)),
        )
        Spacer(Modifier.size(16.dp))
        Text(
            album.name,
            style = MaterialTheme.typography.titleLarge,
            textAlign = TextAlign.Center,
            maxLines = 2,
            overflow = TextOverflow.Ellipsis,
        )
        album.albumArtist?.let {
            Text(
                it,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                textAlign = TextAlign.Center,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
            )
        }
        AudioFormatPills(pills = audioFormatPills(format), modifier = Modifier.padding(top = 10.dp))
        Spacer(Modifier.size(16.dp))
        Row(
            horizontalArrangement = Arrangement.spacedBy(12.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Button(onClick = onPlay) {
                Icon(Icons.Filled.PlayArrow, contentDescription = null)
                Spacer(Modifier.width(8.dp))
                Text("Play")
            }
            FilledTonalIconButton(onClick = onQueue) {
                Icon(Icons.Filled.Add, contentDescription = "Add album to queue")
            }
        }
    }
}

/**
 * A single track row. Tapping plays the album from this track onward (replacing the queue); swiping
 * right queues just this track and swiping left plays it next — the same gestures as the album
 * library, scoped to this one track. The leading slot shows the track number (all tracks share the
 * album's cover, so a per-track thumbnail would be redundant here).
 */
@Composable
private fun TrackRow(
    track: MpdSong,
    onPlay: () -> Unit,
    onQueue: () -> Unit,
    onPlayNext: () -> Unit,
) {
    QueueSwipeRow(onAddToQueue = onQueue, onPlayNext = onPlayNext) {
        ListItem(
            modifier = Modifier.clickable(onClick = onPlay),
            leadingContent = {
                Box(Modifier.size(40.dp), contentAlignment = Alignment.Center) {
                    Text(
                        trackNumberOf(track)?.toString() ?: "•",
                        style = MaterialTheme.typography.bodyMedium,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                    )
                }
            },
            headlineContent = {
                Text(track.title ?: track.uri, maxLines = 1, overflow = TextOverflow.Ellipsis)
            },
            supportingContent =
                track.artist?.let {
                    { Text(it, maxLines = 1, overflow = TextOverflow.Ellipsis) }
                },
            trailingContent = track.duration?.let { { Text(formatDuration(it)) } },
        )
    }
}

/** A light section header separating discs in a multi-disc album. */
@Composable
private fun DiscHeader(disc: Int) {
    Text(
        "Disc $disc",
        style = MaterialTheme.typography.labelMedium,
        color = MaterialTheme.colorScheme.primary,
        modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 16.dp, bottom = 4.dp),
    )
}

/** Leading track number from a `Track` tag (`"3"`, `"3/12"`, …), or null if absent. */
private fun trackNumberOf(track: MpdSong): Int? =
    track.track?.takeWhile { it.isDigit() }?.toIntOrNull()

/** Leading disc number from a `Disc` tag; defaults to 1 when untagged. */
private fun discNumberOf(track: MpdSong): Int =
    track.disc?.takeWhile { it.isDigit() }?.toIntOrNull() ?: 1

/**
 * The album's representative audio format: the most common per-track `Format`
 * (`samplerate:bits:channels`) among the loaded tracks. Null when no track reports one. Using the
 * mode keeps a stray transcoded/hidden track from misrepresenting the album.
 */
private fun representativeFormat(tracks: List<MpdSong>): String? =
    tracks
        .mapNotNull { it.format }
        .groupingBy { it }
        .eachCount()
        .maxByOrNull { it.value }
        ?.key
