package com.keylightpiano.pianotimeline.ui

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.keylightpiano.pianotimeline.R
import com.keylightpiano.pianotimeline.domain.Recording

@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun RecordingListScreen(
    vm: RecordingListViewModel,
    onOpenSettings: () -> Unit,
    modifier: Modifier = Modifier
) {
    val syncState by vm.syncState.collectAsStateWithLifecycle()
    val recordings by vm.recordings.collectAsStateWithLifecycle()
    val isRefreshing by vm.isRefreshing.collectAsStateWithLifecycle()
    val playbackState by vm.playbackState.collectAsStateWithLifecycle()
    val shuffle by vm.shuffle.collectAsStateWithLifecycle()
    val repeatMode by vm.repeatMode.collectAsStateWithLifecycle()
    val context = LocalContext.current
    val searchQuery by vm.searchQuery.collectAsStateWithLifecycle()
    val sortMode by vm.sortMode.collectAsStateWithLifecycle()
    val hiddenArtists by vm.hiddenArtists.collectAsStateWithLifecycle()
    val availableArtists by vm.availableArtists.collectAsStateWithLifecycle()
    val hiddenYears by vm.hiddenYears.collectAsStateWithLifecycle()
    val availableYears by vm.availableYears.collectAsStateWithLifecycle()
    val isOnline by vm.isOnline.collectAsStateWithLifecycle()

    var showControls by rememberSaveable { mutableStateOf(false) }
    var showSortMenu by remember { mutableStateOf(false) }
    var showArtistFilter by rememberSaveable { mutableStateOf(false) }
    var showYearFilter by rememberSaveable { mutableStateOf(false) }
    var showOverflowMenu by remember { mutableStateOf(false) }

    val listState = rememberLazyListState()

    // Jump back to the TOP of the list whenever the ordering or the active
    // filters change — otherwise the LazyColumn keeps its old scroll offset and
    // can end up stranded (e.g. mid-list or at the bottom) after the set of
    // visible items shrinks then grows again.
    LaunchedEffect(sortMode, hiddenArtists, hiddenYears) {
        listState.scrollToItem(0)
    }

    // After a pull-to-refresh finishes, scroll to top so any newly-pulled entries
    // (which appear at the top, newest-first) are immediately visible.
    var wasRefreshing by remember { mutableStateOf(false) }
    LaunchedEffect(isRefreshing) {
        if (wasRefreshing && !isRefreshing) {
            listState.animateScrollToItem(0)
        }
        wasRefreshing = isRefreshing
    }

    // Favoriting/unfavoriting reorders the list. animateItem() animates the
    // toggled card to its new spot, but we also want the VIEWPORT to stay put so
    // the list doesn't jump to follow an item that moved far away (e.g.
    // unfavoriting something near the top sends it way down).
    //
    // Strategy: just before toggling, remember the id + pixel offset of the item
    // currently at the top of the viewport. After the list reorders, scroll back
    // to that same item. Because the anchor is a STABLE item (not the one that
    // moved), the restore lands cleanly and the animation still plays.
    var pendingAnchor by remember { mutableStateOf<Pair<String, Int>?>(null) }

    val onFavoriteToggle: (Recording) -> Unit = { recording ->
        // Anchor to the first visible item that ISN'T the one being toggled —
        // anchoring to the toggled item would make the viewport chase it as it
        // moves. Fall back to no anchor if the toggled item is the only visible one.
        val firstVisibleIndex = listState.firstVisibleItemIndex
        val anchorId = (firstVisibleIndex until recordings.size)
            .asSequence()
            .map { recordings[it].id }
            .firstOrNull { it != recording.id }
        if (anchorId != null) {
            val offset = if (recordings.getOrNull(firstVisibleIndex)?.id == anchorId)
                listState.firstVisibleItemScrollOffset else 0
            pendingAnchor = anchorId to offset
        }
        vm.toggleFavorite(recording)
    }

    // Restore the viewport to the anchored item once the reordered list arrives.
    LaunchedEffect(recordings) {
        val anchor = pendingAnchor ?: return@LaunchedEffect
        val (anchorId, offset) = anchor
        val newIndex = recordings.indexOfFirst { it.id == anchorId }
        if (newIndex >= 0) {
            listState.scrollToItem(newIndex, offset)
        }
        pendingAnchor = null
    }

    // Deep link: when the VM requests scrolling to a specific entry, find its
    // index in the current visible list and scroll there.
    val scrollToId by vm.scrollToId.collectAsStateWithLifecycle()
    LaunchedEffect(scrollToId, recordings) {
        val id = scrollToId ?: return@LaunchedEffect
        val index = recordings.indexOfFirst { it.id == id }
        if (index >= 0) {
            listState.animateScrollToItem(index)
            vm.consumeScrollTarget()
        }
    }

    // Deep link: a .mid link requests the falling-notes view. That screen doesn't
    // exist yet, so for now we just consume the request (the list still scrolls to
    // the entry via scrollToId above). Wire navigation here when the view is built.
    val openVisualizationFor by vm.openVisualizationFor.collectAsStateWithLifecycle()
    LaunchedEffect(openVisualizationFor) {
        if (openVisualizationFor != null) {
            // TODO: navigate to falling-notes view for openVisualizationFor
            vm.consumeVisualizationRequest()
        }
    }

    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
    val hasActiveFilter = searchQuery.isNotEmpty() || hiddenArtists.isNotEmpty() || hiddenYears.isNotEmpty()

    // The recording matching the active track, if any — used by the mini-player.
    // We look in the full unfiltered list so the bar stays even if a filter would
    // hide the playing track.
    val nowPlaying = remember(playbackState.nowPlayingId, recordings) {
        playbackState.nowPlayingId?.let { id -> recordings.firstOrNull { it.id == id } }
    }

    Scaffold(
        modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
        topBar = {
            TopAppBar(
                title = {
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        Image(
                            painter = painterResource(R.drawable.logo),
                            contentDescription = null,  // decorative; title text conveys meaning
                            modifier = Modifier
                                .size(28.dp)
                                .padding(end = 8.dp)
                        )
                        Text(stringResource(R.string.title_app), fontWeight = FontWeight.Bold)
                    }
                },
                actions = {
                    // Search & filter
                    IconButton(onClick = { showControls = !showControls }) {
                        Icon(
                            imageVector = Icons.Default.Search,
                            contentDescription = stringResource(R.string.cd_search_filter),
                            tint = if (hasActiveFilter || showControls) Color(0xFF70D66B)
                                   else MaterialTheme.colorScheme.onSurfaceVariant
                        )
                    }

                    // Overflow (three dots) → Settings
                    Box {
                        IconButton(onClick = { showOverflowMenu = true }) {
                            Icon(
                                imageVector = Icons.Default.MoreVert,
                                contentDescription = stringResource(R.string.cd_more_options),
                                tint = MaterialTheme.colorScheme.onSurfaceVariant
                            )
                        }
                        DropdownMenu(
                            expanded = showOverflowMenu,
                            onDismissRequest = { showOverflowMenu = false }
                        ) {
                            DropdownMenuItem(
                                text = { Text(stringResource(R.string.settings_title)) },
                                onClick = {
                                    showOverflowMenu = false
                                    onOpenSettings()
                                }
                            )
                        }
                    }
                },
                scrollBehavior = scrollBehavior
            )
        },
        bottomBar = {
            Column {
                MiniPlayer(
                    recording = nowPlaying,   // null = stopped/empty state
                    playback = playbackState,
                    onTogglePlayPause = { vm.togglePlayPause() },
                    onSeek = { vm.seekTo(it) },
                    onPrevious = { vm.skipPrevious() },
                    onNext = { vm.skipNext() },
                    onStop = { vm.stopPlayback() },
                    shuffle = shuffle,
                    repeatMode = repeatMode,
                    onToggleShuffle = { vm.toggleShuffle() },
                    onCycleRepeat = { vm.cycleRepeatMode() },
                    // When the offline banner is showing below, IT owns the
                    // nav-bar inset, so the mini-player must not also apply it.
                    applyNavigationBarInset = isOnline
                )
                OfflineBanner(visible = !isOnline)
            }
        }
    ) { innerPadding ->
        when (val s = syncState) {
            is SyncState.Syncing -> {
                Box(Modifier.fillMaxSize().padding(innerPadding), contentAlignment = Alignment.Center) {
                    Column(horizontalAlignment = Alignment.CenterHorizontally) {
                        CircularProgressIndicator()
                        Spacer(Modifier.height(12.dp))
                        Text(stringResource(R.string.syncing), style = MaterialTheme.typography.bodyMedium)
                    }
                }
            }

            is SyncState.Error -> {
                Box(Modifier.fillMaxSize().padding(innerPadding), contentAlignment = Alignment.Center) {
                    Column(
                        horizontalAlignment = Alignment.CenterHorizontally,
                        verticalArrangement = Arrangement.spacedBy(12.dp),
                        modifier = Modifier.padding(24.dp)
                    ) {
                        Text(stringResource(R.string.error_load_title), style = MaterialTheme.typography.titleMedium)
                        Text(s.message, style = MaterialTheme.typography.bodySmall)
                        Button(onClick = { vm.retry() }) { Text(stringResource(R.string.retry)) }
                    }
                }
            }

            is SyncState.Ready -> {
                Column(modifier = Modifier.fillMaxSize().padding(innerPadding)) {
                    AnimatedVisibility(
                        visible = showControls,
                        enter = expandVertically(),
                        exit = shrinkVertically()
                    ) {
                        Column(modifier = Modifier.fillMaxWidth()) {
                            OutlinedTextField(
                                value = searchQuery,
                                onValueChange = { vm.setSearchQuery(it) },
                                modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp),
                                placeholder = { Text(stringResource(R.string.search_placeholder)) },
                                leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
                                trailingIcon = {
                                    if (searchQuery.isNotEmpty()) {
                                        IconButton(onClick = { vm.setSearchQuery("") }) {
                                            Icon(Icons.Default.Clear, contentDescription = stringResource(R.string.cd_clear))
                                        }
                                    }
                                },
                                singleLine = true,
                                shape = MaterialTheme.shapes.medium
                            )

                            Row(
                                modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp),
                                horizontalArrangement = Arrangement.SpaceBetween,
                                verticalAlignment = Alignment.CenterVertically
                            ) {
                                Box {
                                    TextButton(onClick = { showSortMenu = true }) {
                                        Text(stringResource(sortMode.labelRes), style = MaterialTheme.typography.labelMedium,
                                            color = Color(0xFF70D66B))
                                    }
                                    DropdownMenu(expanded = showSortMenu, onDismissRequest = { showSortMenu = false }) {
                                        SortMode.entries.forEach { mode ->
                                            DropdownMenuItem(
                                                text = {
                                                    Text(
                                                        stringResource(mode.labelRes),
                                                        fontWeight = if (mode == sortMode) FontWeight.Bold else FontWeight.Normal
                                                    )
                                                },
                                                onClick = {
                                                    vm.setSortMode(mode)
                                                    showSortMenu = false
                                                }
                                            )
                                        }
                                    }
                                }

                                TextButton(
                                    onClick = {
                                        showArtistFilter = !showArtistFilter
                                        if (showArtistFilter) showYearFilter = false
                                    },
                                    modifier = Modifier.background(
                                        color = if (showArtistFilter) Color(0xFF70D66B).copy(alpha = 0.18f)
                                                else Color.Transparent,
                                        shape = RoundedCornerShape(50)
                                    )
                                ) {
                                    Text(
                                        if (hiddenArtists.isEmpty()) stringResource(R.string.filter_artists)
                                        else stringResource(R.string.filter_artists_hidden, hiddenArtists.size),
                                        style = MaterialTheme.typography.labelMedium,
                                        color = Color(0xFF70D66B)
                                    )
                                }

                                TextButton(
                                    onClick = {
                                        showYearFilter = !showYearFilter
                                        if (showYearFilter) showArtistFilter = false
                                    },
                                    modifier = Modifier.background(
                                        color = if (showYearFilter) Color(0xFF70D66B).copy(alpha = 0.18f)
                                                else Color.Transparent,
                                        shape = RoundedCornerShape(50)
                                    )
                                ) {
                                    Text(
                                        if (hiddenYears.isEmpty()) stringResource(R.string.filter_years)
                                        else stringResource(R.string.filter_years_hidden, hiddenYears.size),
                                        style = MaterialTheme.typography.labelMedium,
                                        color = Color(0xFF70D66B)
                                    )
                                }
                            }

                            AnimatedVisibility(
                                visible = showArtistFilter,
                                enter = expandVertically(),
                                exit = shrinkVertically()
                            ) {
                                Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)) {
                                    Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                                        TextButton(onClick = { vm.showAllArtists() }) {
                                            Text(stringResource(R.string.filter_all), style = MaterialTheme.typography.labelSmall,
                                                color = Color(0xFF70D66B))
                                        }
                                        TextButton(onClick = { vm.hideAllArtists() }) {
                                            Text(stringResource(R.string.filter_none), style = MaterialTheme.typography.labelSmall,
                                                color = Color(0xFF70D66B))
                                        }
                                    }
                                    // Fixed 3-per-row grid: every chip takes an
                                    // equal third of the width so they line up in a
                                    // regular grid spanning the screen. Long artist
                                    // names scroll (marquee) within their cell.
                                    val artistRows = availableArtists.chunked(3)
                                    Column(verticalArrangement = Arrangement.spacedBy(1.dp)) {
                                        artistRows.forEach { rowArtists ->
                                            Row(
                                                modifier = Modifier.fillMaxWidth(),
                                                horizontalArrangement = Arrangement.spacedBy(6.dp)
                                            ) {
                                                rowArtists.forEach { artist ->
                                                    FilterChip(
                                                        selected = artist !in hiddenArtists,
                                                        onClick = { vm.toggleArtist(artist) },
                                                        modifier = Modifier
                                                            .weight(1f)
                                                            .height(30.dp),
                                                        label = {
                                                            Text(
                                                                artist,
                                                                style = MaterialTheme.typography.labelSmall,
                                                                color = Color(0xFF70D66B),
                                                                maxLines = 1,
                                                                modifier = Modifier
                                                                    .fillMaxWidth()
                                                                    .basicMarquee(
                                                                        iterations = Int.MAX_VALUE,
                                                                        repeatDelayMillis = 2000,
                                                                        initialDelayMillis = 3000,
                                                                        velocity = 30.dp
                                                                    )
                                                            )
                                                        }
                                                    )
                                                }
                                                // Pad the final row so a trailing
                                                // 1- or 2-chip row keeps grid alignment.
                                                repeat(3 - rowArtists.size) {
                                                    Spacer(Modifier.weight(1f))
                                                }
                                            }
                                        }
                                    }
                                }
                            }

                            AnimatedVisibility(
                                visible = showYearFilter,
                                enter = expandVertically(),
                                exit = shrinkVertically()
                            ) {
                                Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)) {
                                    Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                                        TextButton(onClick = { vm.showAllYears() }) {
                                            Text(stringResource(R.string.filter_all), style = MaterialTheme.typography.labelSmall,
                                                color = Color(0xFF70D66B))
                                        }
                                        TextButton(onClick = { vm.hideAllYears() }) {
                                            Text(stringResource(R.string.filter_none), style = MaterialTheme.typography.labelSmall,
                                                color = Color(0xFF70D66B))
                                        }
                                    }
                                    FlowRow(
                                        horizontalArrangement = Arrangement.spacedBy(6.dp),
                                        verticalArrangement = Arrangement.spacedBy(2.dp)
                                    ) {
                                        availableYears.forEach { year ->
                                            FilterChip(
                                                selected = year !in hiddenYears,
                                                onClick = { vm.toggleYear(year) },
                                                label = { Text(year.toString(), style = MaterialTheme.typography.labelSmall,
                                                    color = Color(0xFF70D66B)) }
                                            )
                                        }
                                    }
                                }
                            }

                            HorizontalDivider(modifier = Modifier.padding(top = 8.dp))
                        }
                    }

                    Text(
                        text = stringResource(R.string.recordings_count, recordings.size),
                        style = MaterialTheme.typography.labelSmall,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                        modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp)
                    )

                    PullToRefreshBox(
                        isRefreshing = isRefreshing,
                        onRefresh = { vm.refresh() },
                        modifier = Modifier.fillMaxSize()
                    ) {
                        LazyColumn(
                            state = listState,
                            modifier = Modifier
                                .fillMaxSize()
                                .fadingScrollbar(listState),
                            contentPadding = PaddingValues(start = 12.dp, end = 12.dp, bottom = 80.dp),
                            verticalArrangement = Arrangement.spacedBy(6.dp)
                        ) {
                            items(items = recordings, key = { it.id }) { recording ->
                                RecordingItem(
                                    recording = recording,
                                    isPlaying = playbackState.nowPlayingId == recording.id
                                                && playbackState.isPlaying,
                                    isDisabledOffline = !isOnline && !recording.isCached,
                                    onPlayClick = { vm.onPlay(recording) },
                                    onFavoriteClick = { onFavoriteToggle(recording) },
                                    onNotesClick = if (recording.hasMidi) {
                                        { /* TODO: open falling notes */ }
                                    } else null,
                                    onShareAudio = {
                                        shareLink(context, vm.audioShareUrl(recording))
                                    },
                                    onShareVisualization = if (recording.hasMidi) {
                                        { vm.midiShareUrl(recording)?.let { shareLink(context, it) } }
                                    } else null,
                                    onCopyLink = {
                                        copyLink(context, vm.audioShareUrl(recording))
                                    },
                                    onMarkAsNew = { vm.markAsNew(recording) },
                                    modifier = Modifier.animateItem()
                                )
                            }
                        }
                    }
                }
            }
        }
    }
}

/**
 * Thin bar that slides up from the bottom (under the mini-player) whenever the
 * device is offline. Dark red, small white centered text — Spotify-style.
 * Respects the navigation-bar inset so the text isn't tucked under system nav.
 */
@Composable
private fun OfflineBanner(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
        enter = expandVertically(),
        exit = shrinkVertically()
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .background(Color(0xFF8B0000))  // dark red
                .windowInsetsPadding(WindowInsets.navigationBars)
                .padding(vertical = 4.dp),
            contentAlignment = Alignment.Center
        ) {
            Text(
                text = stringResource(R.string.offline_banner),
                color = Color.White,
                fontSize = 11.sp,
                textAlign = TextAlign.Center,
                modifier = Modifier.fillMaxWidth()
            )
        }
    }
}

// ── Share / clipboard helpers ──────────────────────────────────────────

/** Fire Android's standard share sheet with a media URL as plain text. */
private fun shareLink(context: android.content.Context, url: String) {
    val sendIntent = android.content.Intent(android.content.Intent.ACTION_SEND).apply {
        type = "text/plain"
        putExtra(android.content.Intent.EXTRA_TEXT, url)
    }
    val chooser = android.content.Intent.createChooser(sendIntent, null)
    // Needed because we may be starting from a non-Activity context wrapper.
    chooser.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
    context.startActivity(chooser)
}

/** Copy a URL to the clipboard. */
private fun copyLink(context: android.content.Context, url: String) {
    val clipboard = context.getSystemService(android.content.Context.CLIPBOARD_SERVICE)
        as android.content.ClipboardManager
    clipboard.setPrimaryClip(android.content.ClipData.newPlainText("link", url))
    // On Android 13+ the system shows its own "copied" confirmation; on older
    // versions a Toast is the norm.
    if (android.os.Build.VERSION.SDK_INT < 33) {
        android.widget.Toast.makeText(context, context.getString(R.string.toast_link_copied), android.widget.Toast.LENGTH_SHORT).show()
    }
}
