package com.fabledsword.minstrel.player.output

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.composables.icons.lucide.Circle
import com.composables.icons.lucide.CircleCheck
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Settings
import com.composables.icons.lucide.WifiOff

/**
 * Material 3 ModalBottomSheet listing the available output routes.
 * Tap a row to select + dismiss. Selected route shows the accent
 * CircleCheck; others show an empty Circle. Long route names
 * truncate cleanly via maxLines = 2 + Ellipsis.
 *
 * Two footer hints, each driven by a flag the host screen owns:
 *  - [permissionDenied] — NowPlayingScreen owns the BLUETOOTH_CONNECT
 *    request flow and sets this true on denial so the user sees the
 *    "pair in Settings" affordance.
 *  - [noUpnpDiscovered] — set true when the picker has been open for
 *    a few seconds and no UPnP renderers have arrived; surfaces the
 *    "router may be blocking multicast" hint. Defaults to false so
 *    existing call sites that haven't wired the discovery-timing
 *    logic continue to render without the hint.
 *
 * Keeping the hint flags external preserves this composable's
 * focus-on-rendering shape.
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun OutputPickerSheet(
    snapshot: RouteSnapshot,
    permissionDenied: Boolean,
    onRouteSelected: (OutputRoute) -> Unit,
    onDismiss: () -> Unit,
    noUpnpDiscovered: Boolean = false,
) {
    val sheetState = rememberModalBottomSheetState()
    ModalBottomSheet(
        onDismissRequest = onDismiss,
        sheetState = sheetState,
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 24.dp, vertical = 8.dp),
            verticalArrangement = Arrangement.spacedBy(4.dp),
        ) {
            Text(
                text = "Output",
                style = MaterialTheme.typography.titleMedium,
                color = MaterialTheme.colorScheme.onSurface,
                modifier = Modifier.padding(vertical = 8.dp),
            )
            LazyColumn(
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(max = ROUTE_LIST_MAX_HEIGHT_DP.dp),
                verticalArrangement = Arrangement.spacedBy(4.dp),
            ) {
                items(items = snapshot.available, key = { it.id }) { route ->
                    RouteRow(
                        route = route,
                        isSelected = route.id == snapshot.current.id,
                        onClick = { onRouteSelected(route) },
                    )
                }
            }
            if (permissionDenied) {
                PermissionHintRow()
            }
            if (noUpnpDiscovered) {
                MulticastHintRow()
            }
        }
    }
}

@Composable
private fun RouteRow(
    route: OutputRoute,
    isSelected: Boolean,
    onClick: () -> Unit,
) {
    val tint = if (isSelected) {
        MaterialTheme.colorScheme.primary
    } else {
        MaterialTheme.colorScheme.onSurfaceVariant
    }
    Surface(
        onClick = onClick,
        color = MaterialTheme.colorScheme.surface,
        modifier = Modifier.fillMaxWidth(),
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(vertical = 12.dp),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            Icon(
                imageVector = iconFor(route.kind),
                contentDescription = null,
                tint = tint,
                modifier = Modifier.size(ROW_ICON_DP.dp),
            )
            Column(modifier = Modifier.weight(1f)) {
                Text(
                    text = route.name,
                    style = MaterialTheme.typography.bodyLarge,
                    color = MaterialTheme.colorScheme.onSurface,
                    maxLines = 2,
                    overflow = TextOverflow.Ellipsis,
                )
                val subtitle = route.description ?: defaultSubtitle(route, isSelected)
                Text(
                    text = subtitle,
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
            }
            Icon(
                imageVector = if (isSelected) Lucide.CircleCheck else Lucide.Circle,
                contentDescription = if (isSelected) "Selected" else "Not selected",
                tint = tint,
                modifier = Modifier.size(ROW_ICON_DP.dp),
            )
        }
    }
}

@Composable
private fun PermissionHintRow() {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        Icon(
            imageVector = Lucide.Settings,
            contentDescription = null,
            tint = MaterialTheme.colorScheme.onSurfaceVariant,
            modifier = Modifier.size(HINT_ICON_DP.dp),
        )
        Text(
            text = "Pair a Bluetooth device in Settings to see it here.",
            style = MaterialTheme.typography.bodySmall,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
    }
}

@Composable
private fun MulticastHintRow() {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        Icon(
            imageVector = Lucide.WifiOff,
            contentDescription = null,
            tint = MaterialTheme.colorScheme.onSurfaceVariant,
            modifier = Modifier.size(HINT_ICON_DP.dp),
        )
        Text(
            text = "Your router may be blocking multicast discovery.",
            style = MaterialTheme.typography.bodySmall,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
    }
}

// isSelected (route == the active/selected route) drives the Bluetooth
// "Connected" subtitle. RouteInfo.connectionState can't — it stays
// DISCONNECTED for local SYSTEM routes even when they're in use.
private fun defaultSubtitle(route: OutputRoute, isSelected: Boolean): String = when (route.kind) {
    OutputRoute.Kind.BuiltIn -> "Phone speaker"
    OutputRoute.Kind.Wired -> "Wired"
    OutputRoute.Kind.Bluetooth -> if (isSelected) "Connected" else "Available"
    OutputRoute.Kind.Cast -> "Cast"
    OutputRoute.Kind.Other -> "Available"
}

private const val ROW_ICON_DP = 24
private const val HINT_ICON_DP = 20
private const val ROUTE_LIST_MAX_HEIGHT_DP = 400
