package com.example.ui.screens import androidx.compose.foundation.background 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.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.statusBarsPadding 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.automirrored.filled.ArrowForwardIos import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MusicNote import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlaylistAdd import androidx.compose.material.icons.filled.QueueMusic import androidx.compose.material.icons.filled.Shuffle import androidx.compose.material.icons.filled.Videocam import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember 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.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.media3.common.util.UnstableApi import com.example.data.db.PlaylistEntity import com.example.data.model.MediaItemData import com.example.playback.PlaybackManager import com.example.ui.theme.AccentViolet import com.example.ui.theme.AmoledBlack import com.example.ui.theme.DividerSubtle import com.example.ui.theme.ErrorRed import com.example.ui.theme.SurfaceElevated import com.example.ui.theme.SurfaceSecondary import com.example.ui.theme.TextPrimary import com.example.ui.theme.TextSecondary import com.example.ui.viewmodel.MainViewModel @OptIn(UnstableApi::class) @Composable fun PlaylistsScreen( viewModel: MainViewModel, onCreatePlaylistClick: () -> Unit, onPlayMedia: (MediaItemData) -> Unit, modifier: Modifier = Modifier ) { val playlists by viewModel.playlists.collectAsState() val selectedPlaylistId by viewModel.selectedPlaylistId.collectAsState() val playlistItems by viewModel.playlistItems.collectAsState() val allMedia by viewModel.allMedia.collectAsState() var renameTarget by remember { mutableStateOf(null) } var deleteTarget by remember { mutableStateOf(null) } Column( modifier = modifier .fillMaxSize() .background(AmoledBlack) .statusBarsPadding() .testTag("playlists_screen") ) { if (selectedPlaylistId == null) { // Playlists List Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Text( text = "Playlists", color = TextPrimary, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold ) IconButton( onClick = onCreatePlaylistClick, modifier = Modifier.testTag("playlists_create_btn") ) { Icon(imageVector = Icons.Default.Add, contentDescription = "Create Playlist", tint = AccentViolet) } } if (playlists.isEmpty()) { Box( modifier = Modifier .fillMaxSize() .padding(32.dp), contentAlignment = Alignment.Center ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Icon( imageVector = Icons.Default.QueueMusic, contentDescription = null, tint = AccentViolet, modifier = Modifier.size(56.dp) ) Spacer(modifier = Modifier.height(12.dp)) Text( text = "No playlists created yet.", color = TextSecondary, style = MaterialTheme.typography.bodyMedium ) Spacer(modifier = Modifier.height(16.dp)) Button( onClick = onCreatePlaylistClick, colors = ButtonDefaults.buttonColors(containerColor = AccentViolet) ) { Text("Create Playlist", color = Color.Black, fontWeight = FontWeight.Bold) } } } } else { LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(10.dp) ) { items(playlists, key = { it.playlistId }) { playlist -> var showMenu by remember { mutableStateOf(false) } Surface( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(12.dp)) .clickable { viewModel.selectPlaylist(playlist.playlistId) } .testTag("playlist_item_${playlist.playlistId}"), color = SurfaceSecondary ) { Row( modifier = Modifier.padding(14.dp), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(46.dp) .clip(RoundedCornerShape(10.dp)) .background(SurfaceElevated), contentAlignment = Alignment.Center ) { Icon( imageVector = Icons.Default.QueueMusic, contentDescription = null, tint = AccentViolet, modifier = Modifier.size(26.dp) ) } Spacer(modifier = Modifier.width(14.dp)) Column(modifier = Modifier.weight(1f)) { Text( text = playlist.name, color = TextPrimary, fontSize = 15.sp, fontWeight = FontWeight.SemiBold ) Text( text = "Tap to open", color = TextSecondary, fontSize = 12.sp ) } Box { IconButton(onClick = { showMenu = true }) { Icon(imageVector = Icons.Default.MoreVert, contentDescription = null, tint = TextSecondary) } DropdownMenu( expanded = showMenu, onDismissRequest = { showMenu = false }, modifier = Modifier.background(SurfaceElevated) ) { DropdownMenuItem( text = { Text("Rename", color = TextPrimary) }, onClick = { renameTarget = playlist showMenu = false } ) DropdownMenuItem( text = { Text("Delete", color = ErrorRed) }, onClick = { deleteTarget = playlist showMenu = false } ) } } } } } } } } else { // Selected Playlist Detail val activePlaylist = playlists.find { it.playlistId == selectedPlaylistId } val matchingMedia = playlistItems.mapNotNull { item -> allMedia.find { it.contentUri == item.mediaUri } ?: MediaItemData( id = "pl_${item.id}", contentUri = item.mediaUri, title = item.title, artist = item.artist, album = item.album, durationMs = item.durationMs, isVideo = item.isVideo ) } Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically ) { IconButton( onClick = { viewModel.selectPlaylist(null) }, modifier = Modifier.testTag("playlist_detail_back_btn") ) { Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back", tint = TextPrimary) } Text( text = activePlaylist?.name ?: "Playlist", color = TextPrimary, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold, modifier = Modifier.weight(1f) ) } if (matchingMedia.isNotEmpty()) { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { Surface( onClick = { PlaybackManager.setQueue(matchingMedia, 0, autoPlay = true) }, shape = RoundedCornerShape(10.dp), color = AccentViolet, modifier = Modifier.weight(1f) ) { Row( modifier = Modifier.padding(vertical = 10.dp), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Icon(imageVector = Icons.Default.PlayArrow, contentDescription = null, tint = AmoledBlack) Spacer(modifier = Modifier.width(6.dp)) Text("Play All (${matchingMedia.size})", color = AmoledBlack, fontWeight = FontWeight.Bold) } } Surface( onClick = { PlaybackManager.toggleShuffle() PlaybackManager.setQueue(matchingMedia.shuffled(), 0, autoPlay = true) }, shape = RoundedCornerShape(10.dp), color = SurfaceElevated, modifier = Modifier.weight(1f) ) { Row( modifier = Modifier.padding(vertical = 10.dp), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Icon(imageVector = Icons.Default.Shuffle, contentDescription = null, tint = AccentViolet) Spacer(modifier = Modifier.width(6.dp)) Text("Shuffle", color = TextPrimary, fontWeight = FontWeight.Bold) } } } } if (matchingMedia.isEmpty()) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("Playlist is empty. Add songs from your library.", color = TextSecondary) } } else { LazyColumn(modifier = Modifier.fillMaxSize()) { items(matchingMedia, key = { it.contentUri }) { item -> Row( modifier = Modifier .fillMaxWidth() .clickable { onPlayMedia(item) } .padding(horizontal = 16.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = if (item.isVideo) Icons.Default.Videocam else Icons.Default.MusicNote, contentDescription = null, tint = AccentViolet, modifier = Modifier.size(24.dp) ) Spacer(modifier = Modifier.width(14.dp)) Column(modifier = Modifier.weight(1f)) { Text(text = item.title, color = TextPrimary, fontSize = 14.sp, fontWeight = FontWeight.Medium) Text(text = "${item.artist} • ${item.formattedDuration}", color = TextSecondary, fontSize = 12.sp) } IconButton( onClick = { viewModel.removeFromPlaylist(selectedPlaylistId!!, item.contentUri) } ) { Icon(imageVector = Icons.Default.Delete, contentDescription = "Remove", tint = TextSecondary) } } } } } } } // Rename Dialog renameTarget?.let { pl -> var newName by remember { mutableStateOf(pl.name) } AlertDialog( onDismissRequest = { renameTarget = null }, confirmButton = { Button( onClick = { viewModel.renamePlaylist(pl.playlistId, newName) renameTarget = null }, colors = ButtonDefaults.buttonColors(containerColor = AccentViolet) ) { Text("Save", color = Color.Black, fontWeight = FontWeight.Bold) } }, dismissButton = { TextButton(onClick = { renameTarget = null }) { Text("Cancel", color = TextSecondary) } }, title = { Text("Rename Playlist", color = TextPrimary) }, text = { OutlinedTextField( value = newName, onValueChange = { newName = it }, singleLine = true, colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = AccentViolet, unfocusedBorderColor = DividerSubtle, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ) ) }, containerColor = SurfaceElevated ) } // Delete Confirmation Dialog deleteTarget?.let { pl -> AlertDialog( onDismissRequest = { deleteTarget = null }, confirmButton = { Button( onClick = { viewModel.deletePlaylist(pl.playlistId) deleteTarget = null }, colors = ButtonDefaults.buttonColors(containerColor = ErrorRed) ) { Text("Delete", color = Color.White) } }, dismissButton = { TextButton(onClick = { deleteTarget = null }) { Text("Cancel", color = TextSecondary) } }, title = { Text("Delete Playlist?", color = TextPrimary) }, text = { Text( "Are you sure you want to delete \"${pl.name}\"? Your original media files will NOT be deleted.", color = TextSecondary ) }, containerColor = SurfaceElevated ) } }