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.PaddingValues
import androidx.compose.foundation.layout.aspectRatio
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.GridCells
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.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalTextStyle
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.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
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.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import ca.ksamad.encore.data.AlbumViewMode
import ca.ksamad.encore.mpd.model.MpdAlbum
import ca.ksamad.encore.playback.AlbumArt
import kotlinx.coroutines.launch

/**
 * Browse every album on the server. Tapping one opens its detail screen; swiping a row queues the
 * whole album (right) or plays it next (left).
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AlbumsScreen(
    vm: PlayerViewModel,
    query: String,
    onQueryChange: (String) -> Unit,
    listState: LazyListState,
    gridState: LazyGridState,
    onBack: () -> Unit,
    onOpenAlbum: (MpdAlbum) -> Unit,
) {
    val viewMode by vm.albumViewMode.collectAsStateWithLifecycle()
    val gridColumns by vm.gridColumns.collectAsStateWithLifecycle()

    // null = still loading.
    val albums by
        produceState<List<MpdAlbum>?>(initialValue = null) {
            value =
                vm.loadAlbums()
                    .sortedWith(
                        compareBy({ it.albumArtist?.lowercase() ?: "" }, { it.name.lowercase() })
                    )
        }

    // The search text is hoisted (owned by EncoreApp) so it survives drilling into
    // an album and coming back. Whether the search bar is *open* is local, but it
    // starts open whenever there's a restored query so returning re-shows the search.
    var searching by remember { mutableStateOf(query.isNotEmpty()) }

    // Transient confirmation for the swipe actions (add-to-queue / play-next).
    val snackbarHostState = remember { SnackbarHostState() }
    val scope = rememberCoroutineScope()

    fun flash(message: String) {
        // Replace any in-flight snackbar so rapid swipes feel snappy, not queued.
        snackbarHostState.currentSnackbarData?.dismiss()
        scope.launch { snackbarHostState.showSnackbar(message) }
    }

    val current = albums
    val filtered =
        remember(current, query) {
            // Match every whitespace-separated term against the album title + artist
            // together, so a query can span both fields ("beatles abbey") and word
            // order doesn't matter.
            val terms = query.trim().lowercase().split(Regex("\\s+")).filter { it.isNotEmpty() }
            when {
                current == null -> {
                    null
                }

                terms.isEmpty() -> {
                    current
                }

                else -> {
                    current.filter { album ->
                        val haystack = "${album.name} ${album.albumArtist ?: ""}".lowercase()
                        terms.all { haystack.contains(it) }
                    }
                }
            }
        }

    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    if (searching) {
                        AlbumSearchField(query = query, onQueryChange = onQueryChange)
                    } else {
                        Text(filtered?.let { "Albums (${it.size})" } ?: "Albums")
                    }
                },
                navigationIcon = {
                    IconButton(
                        onClick = {
                            if (searching) {
                                searching = false
                                onQueryChange("")
                            } else {
                                onBack()
                            }
                        }
                    ) {
                        Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
                    }
                },
                actions = {
                    if (searching) {
                        IconButton(onClick = { onQueryChange("") }, enabled = query.isNotEmpty()) {
                            Icon(Icons.Filled.Clear, contentDescription = "Clear search")
                        }
                    } else if (current != null) {
                        IconButton(onClick = { searching = true }) {
                            Icon(Icons.Filled.Search, contentDescription = "Search albums")
                        }
                    }
                },
            )
        },
        snackbarHost = { SnackbarHost(snackbarHostState) },
    ) { innerPadding ->
        when {
            filtered == null -> {
                Box(
                    Modifier.fillMaxSize().padding(innerPadding),
                    contentAlignment = Alignment.Center,
                ) {
                    CircularProgressIndicator()
                }
            }

            filtered.isEmpty() -> {
                Box(
                    Modifier.fillMaxSize().padding(innerPadding),
                    contentAlignment = Alignment.Center,
                ) {
                    Text(
                        if (query.isBlank()) "No albums found"
                        else "No albums match \"${query.trim()}\"",
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                    )
                }
            }

            viewMode == AlbumViewMode.Grid -> {
                LazyVerticalGrid(
                    columns = GridCells.Fixed(gridColumns),
                    state = gridState,
                    modifier = Modifier.padding(innerPadding),
                    contentPadding = PaddingValues(12.dp),
                    horizontalArrangement = Arrangement.spacedBy(12.dp),
                    verticalArrangement = Arrangement.spacedBy(16.dp),
                ) {
                    items(filtered, key = { "${it.name} ${it.albumArtist}" }) { album ->
                        AlbumGridCell(album = album, onTap = { onOpenAlbum(album) })
                    }
                }
            }

            else -> {
                LazyColumn(state = listState, modifier = Modifier.padding(innerPadding)) {
                    items(filtered, key = { "${it.name} ${it.albumArtist}" }) { album ->
                        SwipeableAlbumRow(
                            album = album,
                            onTap = { onOpenAlbum(album) },
                            onQueue = {
                                vm.queueAlbum(album.name, album.albumArtist)
                                flash("Added “${album.name}” to the queue")
                            },
                            onPlayNext = {
                                vm.playAlbumNext(album.name, album.albumArtist)
                                flash("“${album.name}” will play next")
                            },
                        )
                    }
                }
            }
        }
    }
}

/**
 * One album row. Tapping opens the album ([onTap]); swiping reveals two one-shot actions that leave
 * the row in place — right ([onQueue]) appends the album to the queue, left ([onPlayNext]) inserts
 * it right after the current track.
 */
@Composable
private fun SwipeableAlbumRow(
    album: MpdAlbum,
    onTap: () -> Unit,
    onQueue: () -> Unit,
    onPlayNext: () -> Unit,
) {
    QueueSwipeRow(onAddToQueue = onQueue, onPlayNext = onPlayNext) {
        ListItem(
            modifier = Modifier.clickable(onClick = onTap),
            leadingContent = {
                ArtImage(
                    model = AlbumArt(album.name, album.albumArtist),
                    iconSize = 24.dp,
                    modifier = Modifier.size(48.dp).clip(RoundedCornerShape(6.dp)),
                )
            },
            headlineContent = {
                Text(album.name, maxLines = 1, overflow = TextOverflow.Ellipsis)
            },
            supportingContent =
                album.albumArtist?.let {
                    { Text(it, maxLines = 1, overflow = TextOverflow.Ellipsis) }
                },
        )
    }
}

/**
 * One album tile for the grid layout: square cover art with the album name in bold underneath and
 * the artist in a smaller, muted font below that. Tapping opens the album ([onTap]).
 */
@Composable
private fun AlbumGridCell(
    album: MpdAlbum,
    onTap: () -> Unit,
) {
    Column(modifier = Modifier.clickable(onClick = onTap)) {
        ArtImage(
            model = AlbumArt(album.name, album.albumArtist),
            iconSize = 48.dp,
            modifier =
                Modifier.fillMaxWidth().aspectRatio(1f).clip(RoundedCornerShape(8.dp)),
        )
        Text(
            album.name,
            style = MaterialTheme.typography.bodyMedium,
            fontWeight = FontWeight.Bold,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
            modifier = Modifier.padding(top = 6.dp),
        )
        album.albumArtist?.let {
            Text(
                it,
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
            )
        }
    }
}

/**
 * Inline search box that lives in the top bar while searching. Auto-focuses and opens the keyboard
 * only on a fresh (empty) open — when it reappears already populated (e.g. restored after returning
 * from an album) it shows the query and results without stealing focus. The surrounding [TopAppBar]
 * handles clearing/closing.
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun AlbumSearchField(
    query: String,
    onQueryChange: (String) -> Unit,
) {
    val focusRequester = remember { FocusRequester() }
    val focusManager = LocalFocusManager.current

    TextField(
        value = query,
        onValueChange = onQueryChange,
        modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
        singleLine = true,
        textStyle = LocalTextStyle.current,
        placeholder = { Text("Search albums & artists") },
        keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
        keyboardActions = KeyboardActions(onSearch = { focusManager.clearFocus() }),
        colors =
            TextFieldDefaults.colors(
                focusedContainerColor = Color.Transparent,
                unfocusedContainerColor = Color.Transparent,
                focusedIndicatorColor = Color.Transparent,
                unfocusedIndicatorColor = Color.Transparent,
            ),
    )

    // Only grab focus for a fresh search; a restored (non-empty) query shouldn't pop the keyboard.
    LaunchedEffect(Unit) { if (query.isEmpty()) focusRequester.requestFocus() }
}
