package ca.ksamad.encore.ui

import androidx.compose.foundation.clickable
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.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
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.MoreVert
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Remove
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
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.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import ca.ksamad.encore.data.AlbumViewMode
import ca.ksamad.encore.data.SettingsRepository
import ca.ksamad.encore.mpd.model.MpdStatistics
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.launch

/**
 * Settings: shows the current server and offers to switch servers or reset all saved settings.
 * Reached from the player via the gear icon; [onBack] pops back.
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(
    vm: PlayerViewModel,
    onBack: () -> Unit,
) {
    val settings by vm.settings.collectAsStateWithLifecycle()
    val status by vm.status.collectAsStateWithLifecycle()
    var confirmReset by remember { mutableStateOf(false) }
    var refreshMenuOpen by remember { mutableStateOf(false) }

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

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

    // Server statistics (`stats`), fetched once when the screen opens. `null`
    // means either still loading or unavailable — [statsLoaded] disambiguates.
    var statsLoaded by remember { mutableStateOf(false) }
    val stats by
        produceState<MpdStatistics?>(initialValue = null) {
            value = vm.loadStatistics()
            statsLoaded = true
        }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Settings") },
                navigationIcon = {
                    IconButton(onClick = onBack) {
                        Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
                    }
                },
            )
        },
        snackbarHost = { SnackbarHost(snackbarHostState) },
    ) { innerPadding ->
        Column(
            modifier =
                Modifier.fillMaxSize()
                    .padding(innerPadding)
                    .verticalScroll(rememberScrollState())
                    .padding(horizontal = 8.dp)
        ) {
            Text(
                "Server",
                style = MaterialTheme.typography.titleSmall,
                color = MaterialTheme.colorScheme.primary,
                modifier = Modifier.padding(start = 8.dp, top = 12.dp, bottom = 4.dp),
            )
            ListItem(
                headlineContent = { Text("Host") },
                trailingContent = { Text(settings.host) },
            )
            ListItem(
                headlineContent = { Text("Port") },
                trailingContent = { Text(settings.port.toString()) },
            )

            Text(
                "Playback",
                style = MaterialTheme.typography.titleSmall,
                color = MaterialTheme.colorScheme.primary,
                modifier = Modifier.padding(start = 8.dp, top = 20.dp, bottom = 4.dp),
            )
            // Consume is a live MPD playback option; the switch reflects the
            // server's current state and toggling sends the `consume` command.
            val consumeOn = status?.consume == true
            ListItem(
                modifier = Modifier.clickable { vm.setConsume(!consumeOn) },
                headlineContent = { Text("Consume mode") },
                supportingContent = { Text("Remove each track from the queue after it plays") },
                trailingContent = {
                    Switch(
                        checked = consumeOn,
                        onCheckedChange = { vm.setConsume(it) },
                    )
                },
            )

            // How far the hardware volume keys move the server volume per press.
            val volumeStep by vm.volumeStep.collectAsStateWithLifecycle()
            ListItem(
                headlineContent = { Text("Volume step") },
                supportingContent = { Text("How much each volume-key press changes the volume") },
                trailingContent = {
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        IconButton(
                            onClick = { vm.setVolumeStep(volumeStep - 1) },
                            enabled = volumeStep > SettingsRepository.MIN_VOLUME_STEP,
                        ) {
                            Icon(Icons.Filled.Remove, contentDescription = "Decrease volume step")
                        }
                        Text(
                            "$volumeStep%",
                            style = MaterialTheme.typography.bodyLarge,
                            modifier = Modifier.width(40.dp),
                            textAlign = TextAlign.Center,
                        )
                        IconButton(
                            onClick = { vm.setVolumeStep(volumeStep + 1) },
                            enabled = volumeStep < SettingsRepository.MAX_VOLUME_STEP,
                        ) {
                            Icon(Icons.Filled.Add, contentDescription = "Increase volume step")
                        }
                    }
                },
            )

            Text(
                "Library",
                style = MaterialTheme.typography.titleSmall,
                color = MaterialTheme.colorScheme.primary,
                modifier = Modifier.padding(start = 8.dp, top = 20.dp, bottom = 4.dp),
            )
            val albumViewMode by vm.albumViewMode.collectAsStateWithLifecycle()
            ListItem(
                headlineContent = { Text("Album view") },
                supportingContent = { Text("How the albums library is laid out") },
                trailingContent = {
                    SingleChoiceSegmentedButtonRow {
                        AlbumViewMode.entries.forEachIndexed { index, mode ->
                            SegmentedButton(
                                selected = albumViewMode == mode,
                                onClick = { vm.setAlbumViewMode(mode) },
                                shape =
                                    SegmentedButtonDefaults.itemShape(
                                        index = index,
                                        count = AlbumViewMode.entries.size,
                                    ),
                                icon = {},
                            ) {
                                Text(
                                    when (mode) {
                                        AlbumViewMode.List -> "List"
                                        AlbumViewMode.Grid -> "Grid"
                                    }
                                )
                            }
                        }
                    }
                },
            )

            // Column count only matters for the grid layout, so only offer it there.
            if (albumViewMode == AlbumViewMode.Grid) {
                val gridColumns by vm.gridColumns.collectAsStateWithLifecycle()
                val columnOptions =
                    SettingsRepository.MIN_GRID_COLUMNS..SettingsRepository.MAX_GRID_COLUMNS
                ListItem(
                    headlineContent = { Text("Grid columns") },
                    supportingContent = { Text("Number of albums per row") },
                    trailingContent = {
                        SingleChoiceSegmentedButtonRow {
                            columnOptions.forEachIndexed { index, columns ->
                                SegmentedButton(
                                    selected = gridColumns == columns,
                                    onClick = { vm.setGridColumns(columns) },
                                    shape =
                                        SegmentedButtonDefaults.itemShape(
                                            index = index,
                                            count = columnOptions.count(),
                                        ),
                                    icon = {},
                                ) {
                                    Text(columns.toString())
                                }
                            }
                        }
                    },
                )
            }

            // Ask the server to rescan its music sources. Tap runs a normal (mtime-based)
            // update; the overflow offers a full rescan that also re-reads unchanged files.
            // While a scan runs, `status.updating` drives the "Refreshing…" state.
            val refreshing = status?.updating == true
            ListItem(
                modifier =
                    Modifier.clickable(enabled = !refreshing) {
                        vm.updateDatabase()
                        flash("Library refresh started")
                    },
                headlineContent = { Text("Refresh library") },
                supportingContent = {
                    Text(if (refreshing) "Refreshing…" else "Scan for new and changed files")
                },
                trailingContent = {
                    if (refreshing) {
                        CircularProgressIndicator(modifier = Modifier.size(20.dp))
                    } else {
                        Box {
                            IconButton(onClick = { refreshMenuOpen = true }) {
                                Icon(
                                    Icons.Filled.MoreVert,
                                    contentDescription = "More refresh options",
                                )
                            }
                            DropdownMenu(
                                expanded = refreshMenuOpen,
                                onDismissRequest = { refreshMenuOpen = false },
                            ) {
                                DropdownMenuItem(
                                    text = { Text("Full rescan") },
                                    onClick = {
                                        refreshMenuOpen = false
                                        vm.rescanDatabase()
                                        flash("Full rescan started")
                                    },
                                )
                            }
                        }
                    }
                },
            )

            // Drop Encore's cached covers so corrected art on the server shows up. Most
            // re-tagged/re-imported tracks refresh on their own (their mtime changes the
            // art's cache key); this is for the rest — album covers and bare cover.jpg
            // swaps that don't bump a track's mtime.
            ListItem(
                modifier =
                    Modifier.clickable {
                        vm.refreshArtwork()
                        flash("Artwork cache cleared — covers will reload")
                    },
                headlineContent = { Text("Refresh artwork") },
                supportingContent = { Text("Reload cover art from the server") },
                trailingContent = {
                    Icon(Icons.Filled.Refresh, contentDescription = null)
                },
            )

            Text(
                "Server statistics",
                style = MaterialTheme.typography.titleSmall,
                color = MaterialTheme.colorScheme.primary,
                modifier = Modifier.padding(start = 8.dp, top = 20.dp, bottom = 4.dp),
            )
            when {
                !statsLoaded -> {
                    ListItem(
                        headlineContent = { Text("Loading…") },
                        trailingContent = {
                            CircularProgressIndicator(modifier = Modifier.size(20.dp))
                        },
                    )
                }

                stats == null -> {
                    ListItem(
                        headlineContent = { Text("Statistics unavailable") },
                        supportingContent = { Text("The server didn't report any statistics") },
                    )
                }

                else -> {
                    stats?.let { s ->
                        StatRow("Artists", formatCount(s.artists))
                        StatRow("Albums", formatCount(s.albums))
                        StatRow("Tracks", formatCount(s.songs))
                        StatRow("Library length", formatStatDuration(s.dbPlaytimeSeconds))
                        StatRow("Time played", formatStatDuration(s.playtimeSeconds))
                        StatRow("Server uptime", formatStatDuration(s.uptimeSeconds))
                        StatRow("Last updated", formatTimestamp(s.dbUpdateEpochSeconds))
                    }
                }
            }

            Spacer(Modifier.height(24.dp))

            OutlinedButton(
                onClick = {
                    onBack()
                    vm.disconnect()
                },
                modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp),
            ) {
                Text("Change server")
            }
            Spacer(Modifier.height(8.dp))
            Button(
                onClick = { confirmReset = true },
                colors =
                    ButtonDefaults.buttonColors(
                        containerColor = MaterialTheme.colorScheme.errorContainer,
                        contentColor = MaterialTheme.colorScheme.onErrorContainer,
                    ),
                modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp),
            ) {
                Text("Reset settings")
            }
        }
    }

    if (confirmReset) {
        AlertDialog(
            onDismissRequest = { confirmReset = false },
            title = { Text("Reset settings?") },
            text = { Text("This forgets the saved server and disconnects.") },
            confirmButton = {
                TextButton(
                    onClick = {
                        confirmReset = false
                        onBack()
                        vm.resetSettings()
                    }
                ) {
                    Text("Reset")
                }
            },
            dismissButton = {
                TextButton(onClick = { confirmReset = false }) { Text("Cancel") }
            },
        )
    }
}

/** A label/value line matching the Host/Port rows above. */
@Composable
private fun StatRow(
    label: String,
    value: String,
) {
    ListItem(
        headlineContent = { Text(label) },
        trailingContent = { Text(value) },
    )
}

/** Thousands-grouped count, e.g. `12,345`. */
private fun formatCount(n: Int): String = "%,d".format(n)

/**
 * A duration in seconds as a compact `Xd Yh Zm`, dropping leading zero units but always showing at
 * least minutes. Non-positive values render as an em dash.
 */
private fun formatStatDuration(totalSeconds: Long): String {
    if (totalSeconds <= 0) return "—"
    val days = totalSeconds / 86_400
    val hours = (totalSeconds % 86_400) / 3_600
    val minutes = (totalSeconds % 3_600) / 60
    return buildString {
        if (days > 0) append("${days}d ")
        if (days > 0 || hours > 0) append("${hours}h ")
        append("${minutes}m")
    }
}

/** A Unix epoch (seconds) as a localized date-time, or an em dash if unset. */
private fun formatTimestamp(epochSeconds: Long): String {
    if (epochSeconds <= 0) return "—"
    val format = SimpleDateFormat("MMM d, yyyy HH:mm", Locale.getDefault())
    return format.format(Date(epochSeconds * 1000))
}
