package xyz.cottongin.radio247.ui.screens.nowplaying

import android.app.Activity
import android.content.res.Configuration
import android.graphics.BitmapFactory
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import kotlinx.coroutines.delay
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
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.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
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.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.FastForward
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material.icons.outlined.Schedule
import androidx.compose.material.icons.outlined.Speed
import androidx.compose.material.icons.outlined.Wifi
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
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.draw.clipToBounds
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.palette.graphics.Palette
import coil3.compose.AsyncImage
import coil3.compose.rememberAsyncImagePainter
import coil3.request.ImageRequest
import coil3.request.transformations
import xyz.cottongin.radio247.R
import xyz.cottongin.radio247.RadioApplication
import xyz.cottongin.radio247.ui.util.BlurTransformation
import xyz.cottongin.radio247.audio.IcyMetadata
import xyz.cottongin.radio247.audio.MetadataFormatter
import xyz.cottongin.radio247.audio.StreamInfo
import xyz.cottongin.radio247.service.PlaybackState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Request

private data class DominantColors(
    val background: Color,
    val onBackground: Color,
    val accent: Color
)

private val DefaultColors = DominantColors(
    background = Color.Unspecified,
    onBackground = Color.Unspecified,
    accent = Color.Unspecified
)

private fun contrastingTextColor(bg: Color): Color =
    if (bg.luminance() > 0.5f) Color(0xFF1A1A1A) else Color(0xFFF5F5F5)

private fun borderColor(bg: Color): Color =
    if (bg.luminance() > 0.5f) Color.Black.copy(alpha = 0.15f) else Color.White.copy(alpha = 0.15f)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NowPlayingScreen(
    onBack: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: NowPlayingViewModel = viewModel(
        factory = NowPlayingViewModelFactory(
            LocalContext.current.applicationContext as RadioApplication
        )
    )
) {
    val playbackState by viewModel.playbackState.collectAsState()
    val artworkUrl by viewModel.displayArtworkUrl.collectAsState()
    val displayMetadata by viewModel.displayMetadata.collectAsState()
    val sessionElapsed by viewModel.sessionElapsed.collectAsState()
    val connectionElapsed by viewModel.connectionElapsed.collectAsState()
    val estimatedLatencyMs by viewModel.estimatedLatencyMs.collectAsState()
    val stayConnected by viewModel.stayConnected.collectAsState()
    val bufferMs by viewModel.bufferMs.collectAsState()

    val configuration = LocalConfiguration.current
    val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE

    val context = LocalContext.current
    val fallbackBg = MaterialTheme.colorScheme.surface
    val fallbackOnBg = MaterialTheme.colorScheme.onSurface
    val fallbackAccent = MaterialTheme.colorScheme.primary

    var dominantColors by remember { mutableStateOf(DefaultColors) }

    LaunchedEffect(artworkUrl) {
        val url = artworkUrl
        if (url == null) {
            dominantColors = DefaultColors
            return@LaunchedEffect
        }
        withContext(Dispatchers.IO) {
            try {
                val app = context.applicationContext as RadioApplication
                val req = Request.Builder().url(url).build()
                app.okHttpClient.newCall(req).execute().use { response ->
                    if (response.isSuccessful) {
                        val bitmap = response.body?.byteStream()?.use { stream ->
                            BitmapFactory.decodeStream(stream)
                        }
                        if (bitmap != null) {
                            try {
                                val palette = Palette.from(bitmap)
                                    .clearFilters()
                                    .generate()
                                val swatch = palette.dominantSwatch
                                    ?: palette.vibrantSwatch
                                    ?: palette.mutedSwatch
                                if (swatch != null) {
                                    val bg = Color(swatch.rgb)
                                    val onBg = contrastingTextColor(bg)
                                    val accent = if (bg.luminance() < 0.5f) {
                                        Color(
                                            palette.lightVibrantSwatch?.rgb
                                                ?: palette.lightMutedSwatch?.rgb
                                                ?: 0xFFBB86FC.toInt()
                                        )
                                    } else {
                                        Color(
                                            palette.darkVibrantSwatch?.rgb
                                                ?: palette.darkMutedSwatch?.rgb
                                                ?: 0xFF6200EE.toInt()
                                        )
                                    }
                                    dominantColors = DominantColors(bg, onBg, accent)
                                }
                            } finally {
                                bitmap.recycle()
                            }
                        }
                    }
                }
            } catch (_: Exception) {
                dominantColors = DefaultColors
            }
        }
    }

    val rawBg = if (dominantColors.background != Color.Unspecified) dominantColors.background else fallbackBg
    val rawText = if (dominantColors.onBackground != Color.Unspecified) dominantColors.onBackground else fallbackOnBg
    val rawAccent = if (dominantColors.accent != Color.Unspecified) dominantColors.accent else fallbackAccent

    val bgColor by animateColorAsState(rawBg, tween(600), label = "bg")
    val textColor by animateColorAsState(rawText, tween(600), label = "text")
    val accentColor by animateColorAsState(rawAccent, tween(600), label = "accent")

    // Dynamic status bar + navigation bar coloring
    val view = LocalView.current
    val dimmedBg = bgColor.copy(alpha = 0.85f)
    val savedStatusBarColor = remember { mutableStateOf<Int?>(null) }
    val savedNavBarColor = remember { mutableStateOf<Int?>(null) }
    val savedLightStatusBar = remember { mutableStateOf<Boolean?>(null) }
    val savedLightNavBar = remember { mutableStateOf<Boolean?>(null) }

    if (!view.isInEditMode) {
        val activity = view.context as? Activity
        if (activity != null) {
            DisposableEffect(Unit) {
                val window = activity.window
                savedStatusBarColor.value = window.statusBarColor
                savedNavBarColor.value = window.navigationBarColor
                val controller = WindowInsetsControllerCompat(window, view)
                savedLightStatusBar.value = controller.isAppearanceLightStatusBars
                savedLightNavBar.value = controller.isAppearanceLightNavigationBars
                onDispose {
                    savedStatusBarColor.value?.let { window.statusBarColor = it }
                    savedNavBarColor.value?.let { window.navigationBarColor = it }
                    val c = WindowInsetsControllerCompat(window, view)
                    savedLightStatusBar.value?.let { c.isAppearanceLightStatusBars = it }
                    savedLightNavBar.value?.let { c.isAppearanceLightNavigationBars = it }
                }
            }

            SideEffect {
                val window = activity.window
                val barColor = dimmedBg.toArgb()
                window.statusBarColor = barColor
                window.navigationBarColor = barColor
                val controller = WindowInsetsControllerCompat(window, view)
                val useLightIcons = dimmedBg.luminance() < 0.5f
                controller.isAppearanceLightStatusBars = !useLightIcons
                controller.isAppearanceLightNavigationBars = !useLightIcons
            }
        }
    }

    when (val state = playbackState) {
        is PlaybackState.Connecting,
        is PlaybackState.Playing,
        is PlaybackState.Paused,
        is PlaybackState.Reconnecting -> {
            val station = when (state) {
                is PlaybackState.Connecting -> state.station
                is PlaybackState.Playing -> state.station
                is PlaybackState.Paused -> state.station
                is PlaybackState.Reconnecting -> state.station
                else -> return
            }
            val isPaused = state is PlaybackState.Paused
            val isPlaying = state is PlaybackState.Playing
            val isConnecting = state is PlaybackState.Connecting
            val streamInfo = when (state) {
                is PlaybackState.Playing -> state.streamInfo
                is PlaybackState.Paused -> state.streamInfo
                else -> null
            }

            Box(modifier = modifier.fillMaxSize()) {
                BlurredBackground(artworkUrl = artworkUrl, bgColor = bgColor)

                if (isLandscape) {
                    Column(modifier = Modifier.fillMaxSize()) {
                        TopAppBar(
                            title = { },
                            navigationIcon = {
                                IconButton(onClick = onBack) {
                                    Icon(
                                        Icons.AutoMirrored.Filled.ArrowBack,
                                        contentDescription = "Back"
                                    )
                                }
                            },
                            colors = TopAppBarDefaults.topAppBarColors(
                                containerColor = Color.Transparent,
                                navigationIconContentColor = textColor
                            )
                        )
                        LandscapeContent(
                            artworkUrl = artworkUrl,
                            station = station,
                            metadata = displayMetadata,
                            isPaused = isPaused,
                            isPlaying = isPlaying,
                            isConnecting = isConnecting,
                            sessionElapsed = sessionElapsed,
                            connectionElapsed = connectionElapsed,
                            estimatedLatencyMs = estimatedLatencyMs,
                            streamInfo = streamInfo,
                            stayConnected = stayConnected,
                            bufferMs = bufferMs,
                            viewModel = viewModel,
                            bgColor = bgColor,
                            textColor = textColor,
                            accentColor = accentColor
                        )
                    }
                } else {
                    PortraitContent(
                        artworkUrl = artworkUrl,
                        station = station,
                        metadata = displayMetadata,
                        isPaused = isPaused,
                        isPlaying = isPlaying,
                        isConnecting = isConnecting,
                        sessionElapsed = sessionElapsed,
                        connectionElapsed = connectionElapsed,
                        estimatedLatencyMs = estimatedLatencyMs,
                        streamInfo = streamInfo,
                        stayConnected = stayConnected,
                        bufferMs = bufferMs,
                        viewModel = viewModel,
                        onBack = onBack,
                        bgColor = bgColor,
                        textColor = textColor,
                        accentColor = accentColor
                    )
                }

                OverlayCards(state, station, isConnecting)
            }
        }
        PlaybackState.Idle -> {}
    }
}

@Composable
private fun BlurredBackground(
    artworkUrl: String?,
    bgColor: Color,
    modifier: Modifier = Modifier
) {
    val context = LocalContext.current
    Box(modifier = modifier.fillMaxSize()) {
        if (artworkUrl != null) {
            AsyncImage(
                model = ImageRequest.Builder(context)
                    .data(artworkUrl)
                    .transformations(BlurTransformation(radius = 25, scale = 0.1f))
                    .build(),
                contentDescription = null,
                contentScale = ContentScale.Crop,
                modifier = Modifier.fillMaxSize()
            )
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Black.copy(alpha = 0.15f))
            )
        } else {
            Box(modifier = Modifier.fillMaxSize().background(bgColor))
        }
    }
}

@Composable
private fun ArtworkImage(
    artworkUrl: String?,
    bgColor: Color,
    modifier: Modifier = Modifier
) {
    val bColor = borderColor(bgColor)
    val artShape = RoundedCornerShape(14.dp)

    AnimatedContent(
        targetState = artworkUrl,
        modifier = modifier,
        transitionSpec = {
            fadeIn(tween(600)) togetherWith fadeOut(tween(600))
        },
        label = "artwork-crossfade"
    ) { url ->
        Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            if (url != null) {
                val painter = rememberAsyncImagePainter(model = url)
                val intrinsic = painter.intrinsicSize
                val loaded = intrinsic != androidx.compose.ui.geometry.Size.Unspecified &&
                    intrinsic.width > 0f && intrinsic.height > 0f

                if (loaded) {
                    val aspect = intrinsic.width / intrinsic.height
                    Image(
                        painter = painter,
                        contentDescription = null,
                        contentScale = ContentScale.Fit,
                        modifier = Modifier
                            .aspectRatio(aspect)
                            .clip(artShape)
                            .border(1.dp, bColor, artShape)
                    )
                } else {
                    Image(
                        painter = painter,
                        contentDescription = null,
                        contentScale = ContentScale.Fit,
                        modifier = Modifier.fillMaxSize()
                    )
                }
            } else {
                Image(
                    painter = painterResource(R.drawable.ic_radio_placeholder),
                    contentDescription = null,
                    modifier = Modifier.size(120.dp),
                    colorFilter = ColorFilter.tint(contrastingTextColor(bgColor))
                )
            }
        }
    }
}

@Composable
private fun TickerText(
    text: String,
    style: TextStyle,
    color: Color,
    strokeColor: Color? = null,
    strokeWidth: Float = 2f,
    velocityDpPerSecond: Float = 33f,
    modifier: Modifier = Modifier
) {
    val density = LocalDensity.current
    val textMeasurer = rememberTextMeasurer()

    val textWidthPx = remember(text, style) {
        textMeasurer.measure(text, style, maxLines = 1, softWrap = false).size.width.toFloat()
    }

    var containerWidthPx by remember { mutableStateOf(0f) }

    Box(
        modifier = modifier
            .fillMaxWidth()
            .onSizeChanged { containerWidthPx = it.width.toFloat() }
            .clipToBounds()
    ) {
        val overflowPx = if (containerWidthPx > 0f)
            (textWidthPx - containerWidthPx).coerceAtLeast(0f) else 0f

        if (overflowPx > 0f) {
            key(text) {
                val velocityPxPerMs = with(density) { velocityDpPerSecond.dp.toPx() } / 1000f
                val scrollMs = (textWidthPx / velocityPxPerMs).toInt()

                val offset = remember { Animatable(0f) }
                val alpha = remember { Animatable(0f) }

                LaunchedEffect(textWidthPx, containerWidthPx) {
                    offset.snapTo(0f)
                    alpha.snapTo(0f)
                    alpha.animateTo(1f, tween(300))
                    delay(1500L)

                    while (true) {
                        offset.animateTo(
                            -textWidthPx,
                            tween(scrollMs, easing = LinearEasing)
                        )
                        offset.snapTo(0f)
                        alpha.snapTo(0f)
                        alpha.animateTo(1f, tween(300))
                        delay(500L)
                    }
                }

                Box(
                    modifier = Modifier
                        .wrapContentWidth(align = Alignment.Start, unbounded = true)
                        .align(Alignment.CenterStart)
                        .graphicsLayer {
                            translationX = offset.value
                            this.alpha = alpha.value
                        }
                ) {
                    if (strokeColor != null) {
                        Text(
                            text = text,
                            style = style.merge(TextStyle(drawStyle = Stroke(width = strokeWidth))),
                            color = strokeColor,
                            maxLines = 1,
                            softWrap = false
                        )
                    }
                    Text(
                        text = text,
                        style = style,
                        color = color,
                        maxLines = 1,
                        softWrap = false
                    )
                }
            }
        } else {
            Box(modifier = Modifier.align(Alignment.Center)) {
                if (strokeColor != null) {
                    Text(
                        text = text,
                        style = style.merge(TextStyle(drawStyle = Stroke(width = strokeWidth))),
                        color = strokeColor,
                        maxLines = 1,
                        softWrap = false
                    )
                }
                Text(
                    text = text,
                    style = style,
                    color = color,
                    maxLines = 1,
                    softWrap = false
                )
            }
        }
    }
}

@Composable
private fun TrackInfoSection(
    station: xyz.cottongin.radio247.data.model.Station,
    metadata: IcyMetadata?,
    isPaused: Boolean,
    isConnecting: Boolean,
    textColor: Color,
    bgColor: Color,
    modifier: Modifier = Modifier
) {
    val strokeColor = borderColor(bgColor)
    val trackText = when {
        isConnecting -> "Connecting\u2026"
        isPaused -> "Paused"
        else -> formatTrackInfo(metadata)
    }
    val trackStyle = MaterialTheme.typography.headlineLarge.copy(fontWeight = FontWeight.Bold)
    val trackColor = if (isPaused || isConnecting) textColor.copy(alpha = 0.45f) else textColor

    Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
        Text(
            text = station.name.uppercase(),
            style = MaterialTheme.typography.labelLarge.copy(
                letterSpacing = 3.sp,
                fontWeight = FontWeight.Medium
            ),
            textAlign = TextAlign.Center,
            color = textColor.copy(alpha = 0.5f),
            maxLines = 1,
            overflow = TextOverflow.Ellipsis
        )
        Spacer(modifier = Modifier.height(6.dp))
        TickerText(
            text = trackText,
            style = trackStyle,
            color = trackColor,
            strokeColor = strokeColor
        )
    }
}

@Composable
private fun TimerSection(
    sessionElapsed: Long,
    connectionElapsed: Long,
    estimatedLatencyMs: Long,
    isPaused: Boolean,
    isConnecting: Boolean,
    textColor: Color,
    modifier: Modifier = Modifier
) {
    val dim = textColor.copy(alpha = 0.45f)
    val labelStyle = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Light)
    val iconSize = 14.dp

    Row(
        modifier = modifier,
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Icon(
            Icons.Outlined.Schedule,
            contentDescription = "Session time",
            modifier = Modifier.size(iconSize),
            tint = dim
        )
        Spacer(modifier = Modifier.width(3.dp))
        Text(text = formatElapsed(sessionElapsed), style = labelStyle, color = dim)

        if (!isPaused && !isConnecting) {
            Spacer(modifier = Modifier.width(12.dp))
            Icon(
                Icons.Outlined.Wifi,
                contentDescription = "Connection time",
                modifier = Modifier.size(iconSize),
                tint = dim
            )
            Spacer(modifier = Modifier.width(3.dp))
            Text(text = formatElapsed(connectionElapsed), style = labelStyle, color = dim)

            Spacer(modifier = Modifier.width(12.dp))
            Icon(
                Icons.Outlined.Speed,
                contentDescription = "Latency",
                modifier = Modifier.size(iconSize),
                tint = dim
            )
            Spacer(modifier = Modifier.width(3.dp))
            Text(text = "~${estimatedLatencyMs}ms", style = labelStyle, color = dim)
        }
    }
}

@Composable
private fun QualityBadge(
    streamInfo: StreamInfo?,
    textColor: Color,
    modifier: Modifier = Modifier
) {
    if (streamInfo == null) return
    val dim = textColor.copy(alpha = 0.4f)
    val badgeStyle = MaterialTheme.typography.labelSmall.copy(fontWeight = FontWeight.Medium)

    Row(
        modifier = modifier,
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        if (streamInfo.bitrate != null) {
            Text(text = "${streamInfo.bitrate} kbps", style = badgeStyle, color = dim)
        }
        val codec = when {
            streamInfo.contentType?.contains("mpeg", ignoreCase = true) == true -> "MP3"
            streamInfo.contentType?.contains("aac", ignoreCase = true) == true -> "AAC"
            streamInfo.contentType?.contains("ogg", ignoreCase = true) == true -> "OGG"
            streamInfo.contentType?.contains("flac", ignoreCase = true) == true -> "FLAC"
            else -> null
        }
        if (codec != null) {
            if (streamInfo.bitrate != null) {
                Text(text = " \u00B7 ", style = badgeStyle, color = dim)
            }
            Text(text = codec, style = badgeStyle, color = dim)
        }
        val hasPrevBadge = streamInfo.bitrate != null || codec != null
        if (streamInfo.sampleRate != null) {
            if (hasPrevBadge) {
                Text(text = " \u00B7 ", style = badgeStyle, color = dim)
            }
            val rateLabel = when (streamInfo.sampleRate) {
                44100 -> "44.1 kHz"
                22050 -> "22.05 kHz"
                48000 -> "48 kHz"
                32000 -> "32 kHz"
                24000 -> "24 kHz"
                16000 -> "16 kHz"
                11025 -> "11.025 kHz"
                else -> "${streamInfo.sampleRate} Hz"
            }
            val channelLabel = if (streamInfo.channelCount == 1) "Mono" else "Stereo"
            Text(text = "$rateLabel $channelLabel", style = badgeStyle, color = dim)
        }
        if (streamInfo.ssl) {
            Spacer(modifier = Modifier.width(4.dp))
            Icon(
                Icons.Filled.Lock,
                contentDescription = "Encrypted",
                modifier = Modifier.size(10.dp),
                tint = dim
            )
        }
    }
}

@Composable
private fun TransportControls(
    isPaused: Boolean,
    isPlaying: Boolean,
    viewModel: NowPlayingViewModel,
    accentColor: Color,
    textColor: Color,
    modifier: Modifier = Modifier
) {
    val disabledColor = textColor.copy(alpha = 0.18f)
    val heroSize = 96.dp
    val heroIconSize = 56.dp
    val secondarySize = 64.dp
    val secondaryIconSize = 34.dp
    val spacing = 36.dp
    val heroBorder = borderColor(accentColor)
    val secondaryBorder = textColor.copy(alpha = 0.25f)
    val secondaryFill = textColor.copy(alpha = 0.06f)

    Row(
        modifier = modifier,
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        if (isPaused) {
            Box(
                modifier = Modifier
                    .size(heroSize)
                    .clip(CircleShape)
                    .background(accentColor)
                    .border(1.dp, heroBorder, CircleShape)
                    .clickable { viewModel.resume() },
                contentAlignment = Alignment.Center
            ) {
                Icon(
                    Icons.Filled.PlayArrow,
                    contentDescription = "Resume",
                    modifier = Modifier.size(heroIconSize),
                    tint = contrastingTextColor(accentColor)
                )
            }
        } else {
            Box(
                modifier = Modifier
                    .size(secondarySize)
                    .clip(CircleShape)
                    .background(if (isPlaying) secondaryFill else Color.Transparent)
                    .border(1.dp, if (isPlaying) secondaryBorder else disabledColor, CircleShape)
                    .then(if (isPlaying) Modifier.clickable { viewModel.skipAhead() } else Modifier),
                contentAlignment = Alignment.Center
            ) {
                Icon(
                    Icons.Filled.FastForward,
                    contentDescription = "Skip ahead",
                    modifier = Modifier.size(secondaryIconSize),
                    tint = if (isPlaying) accentColor else disabledColor
                )
            }
            Spacer(modifier = Modifier.width(spacing))
            Box(
                modifier = Modifier
                    .size(heroSize)
                    .clip(CircleShape)
                    .background(if (isPlaying) accentColor else disabledColor)
                    .border(1.dp, if (isPlaying) heroBorder else disabledColor, CircleShape)
                    .then(if (isPlaying) Modifier.clickable { viewModel.pause() } else Modifier),
                contentAlignment = Alignment.Center
            ) {
                Icon(
                    Icons.Filled.Pause,
                    contentDescription = "Pause",
                    modifier = Modifier.size(heroIconSize),
                    tint = if (isPlaying) contrastingTextColor(accentColor) else textColor.copy(alpha = 0.3f)
                )
            }
        }
        Spacer(modifier = Modifier.width(spacing))
        Box(
            modifier = Modifier
                .size(secondarySize)
                .clip(CircleShape)
                .background(secondaryFill)
                .border(1.dp, secondaryBorder, CircleShape)
                .clickable { viewModel.stop() },
            contentAlignment = Alignment.Center
        ) {
            Icon(
                Icons.Filled.Stop,
                contentDescription = "Stop",
                modifier = Modifier.size(secondaryIconSize),
                tint = textColor.copy(alpha = 0.6f)
            )
        }
    }
}

@Composable
private fun SettingsSection(
    stayConnected: Boolean,
    bufferMs: Int,
    viewModel: NowPlayingViewModel,
    textColor: Color,
    accentColor: Color,
    compact: Boolean = false,
    modifier: Modifier = Modifier
) {
    val cardColor = textColor.copy(alpha = 0.12f)
    val labelColor = textColor.copy(alpha = 0.7f)
    val sliderColors = SliderDefaults.colors(
        thumbColor = accentColor,
        activeTrackColor = accentColor,
        inactiveTrackColor = textColor.copy(alpha = 0.12f)
    )
    val switchColors = SwitchDefaults.colors(
        checkedThumbColor = accentColor,
        checkedTrackColor = accentColor.copy(alpha = 0.4f),
        uncheckedThumbColor = textColor.copy(alpha = 0.35f),
        uncheckedTrackColor = textColor.copy(alpha = 0.1f)
    )

    val bColor = borderColor(textColor)
    val settingsShape = RoundedCornerShape(12.dp)
    Surface(
        modifier = modifier
            .widthIn(max = 320.dp)
            .border(1.dp, bColor, settingsShape),
        shape = settingsShape,
        color = cardColor
    ) {
        if (compact) {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 16.dp, vertical = 10.dp),
                horizontalArrangement = Arrangement.spacedBy(16.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                Column(modifier = Modifier.weight(1f)) {
                    Text(
                        text = "Buffer ${bufferMs}ms",
                        style = MaterialTheme.typography.bodyMedium,
                        color = labelColor
                    )
                    Slider(
                        value = bufferMs.toFloat(),
                        onValueChange = { viewModel.setBufferMs(it.toInt()) },
                        valueRange = 0f..500f,
                        steps = 49,
                        modifier = Modifier.fillMaxWidth(),
                        colors = sliderColors
                    )
                }
                Row(verticalAlignment = Alignment.CenterVertically) {
                    Text(
                        text = "Stay on",
                        style = MaterialTheme.typography.bodyMedium,
                        color = labelColor
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Switch(
                        checked = stayConnected,
                        onCheckedChange = { viewModel.toggleStayConnected() },
                        colors = switchColors
                    )
                }
            }
        } else {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 20.dp, vertical = 16.dp)
            ) {
                Text(
                    text = "Buffer ${bufferMs}ms",
                    style = MaterialTheme.typography.bodyLarge,
                    color = labelColor
                )
                Slider(
                    value = bufferMs.toFloat(),
                    onValueChange = { viewModel.setBufferMs(it.toInt()) },
                    valueRange = 0f..500f,
                    steps = 49,
                    modifier = Modifier.fillMaxWidth(),
                    colors = sliderColors
                )
                Spacer(modifier = Modifier.height(4.dp))
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.SpaceBetween
                ) {
                    Text(
                        text = "Stay Connected",
                        style = MaterialTheme.typography.bodyLarge,
                        color = labelColor
                    )
                    Switch(
                        checked = stayConnected,
                        onCheckedChange = { viewModel.toggleStayConnected() },
                        colors = switchColors
                    )
                }
            }
        }
    }
}

@Composable
private fun PortraitContent(
    artworkUrl: String?,
    station: xyz.cottongin.radio247.data.model.Station,
    metadata: IcyMetadata?,
    isPaused: Boolean,
    isPlaying: Boolean,
    isConnecting: Boolean,
    sessionElapsed: Long,
    connectionElapsed: Long,
    estimatedLatencyMs: Long,
    streamInfo: StreamInfo?,
    stayConnected: Boolean,
    bufferMs: Int,
    viewModel: NowPlayingViewModel,
    onBack: () -> Unit,
    bgColor: Color,
    textColor: Color,
    accentColor: Color
) {
    val bColor = borderColor(bgColor)
    val cardShape = RoundedCornerShape(14.dp)
    val backBtnShape = RoundedCornerShape(10.dp)

    val statusBarPadding = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
    val navBarPadding = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()

    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.fillMaxSize()) {
            Box(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxWidth()
                    .padding(start = 24.dp, end = 24.dp, top = statusBarPadding + 48.dp, bottom = 8.dp),
                contentAlignment = Alignment.Center
            ) {
                ArtworkImage(
                    artworkUrl = artworkUrl,
                    bgColor = bgColor,
                    modifier = Modifier.fillMaxSize()
                )
            }

            Box(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxWidth()
                    .padding(horizontal = 12.dp),
                contentAlignment = Alignment.Center
            ) {
                Box(
                    modifier = Modifier
                        .widthIn(min = 340.dp)
                        .clip(cardShape)
                        .background(bgColor.copy(alpha = 0.78f))
                        .border(1.dp, bColor, cardShape)
                ) {
                    Column(
                        modifier = Modifier
                            .align(Alignment.Center)
                            .verticalScroll(rememberScrollState())
                            .padding(horizontal = 24.dp, vertical = 20.dp),
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        TrackInfoSection(station, metadata, isPaused, isConnecting, textColor, bgColor)

                        Spacer(modifier = Modifier.height(8.dp))

                        TimerSection(
                            sessionElapsed, connectionElapsed, estimatedLatencyMs,
                            isPaused, isConnecting, textColor
                        )

                        QualityBadge(streamInfo = streamInfo, textColor = textColor)

                        Spacer(modifier = Modifier.height(24.dp))

                        TransportControls(
                            isPaused = isPaused,
                            isPlaying = isPlaying,
                            viewModel = viewModel,
                            accentColor = accentColor,
                            textColor = textColor
                        )

                        Spacer(modifier = Modifier.height(24.dp))

                        SettingsSection(stayConnected, bufferMs, viewModel, textColor, accentColor, compact = false)
                    }
                }
            }

            Spacer(modifier = Modifier.height(navBarPadding + 8.dp))
        }

        Box(
            modifier = Modifier
                .padding(start = 12.dp, top = statusBarPadding + 12.dp)
                .size(40.dp)
                .clip(backBtnShape)
                .background(bgColor.copy(alpha = 0.5f))
                .border(1.dp, bColor, backBtnShape)
                .clickable { onBack() },
            contentAlignment = Alignment.Center
        ) {
            Icon(
                Icons.AutoMirrored.Filled.ArrowBack,
                contentDescription = "Back",
                tint = textColor,
                modifier = Modifier.size(20.dp)
            )
        }
    }
}

@Composable
private fun LandscapeContent(
    artworkUrl: String?,
    station: xyz.cottongin.radio247.data.model.Station,
    metadata: IcyMetadata?,
    isPaused: Boolean,
    isPlaying: Boolean,
    isConnecting: Boolean,
    sessionElapsed: Long,
    connectionElapsed: Long,
    estimatedLatencyMs: Long,
    streamInfo: StreamInfo?,
    stayConnected: Boolean,
    bufferMs: Int,
    viewModel: NowPlayingViewModel,
    bgColor: Color,
    textColor: Color,
    accentColor: Color
) {
    val bColor = borderColor(bgColor)
    val cardShape = RoundedCornerShape(14.dp)

    Row(
        modifier = Modifier.fillMaxSize(),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Box(
            modifier = Modifier
                .weight(1f)
                .fillMaxHeight()
                .padding(16.dp),
            contentAlignment = Alignment.Center
        ) {
            ArtworkImage(
                artworkUrl = artworkUrl,
                bgColor = bgColor,
                modifier = Modifier.fillMaxSize()
            )
        }

        Box(
            modifier = Modifier
                .weight(1f)
                .fillMaxHeight()
                .padding(12.dp),
            contentAlignment = Alignment.Center
        ) {
            Box(
                modifier = Modifier
                    .widthIn(min = 340.dp)
                    .clip(cardShape)
                    .background(bgColor.copy(alpha = 0.78f))
                    .border(1.dp, bColor, cardShape)
            ) {
                Column(
                    modifier = Modifier
                        .verticalScroll(rememberScrollState())
                        .padding(horizontal = 20.dp, vertical = 16.dp),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    TrackInfoSection(station, metadata, isPaused, isConnecting, textColor, bgColor)

                    Spacer(modifier = Modifier.height(8.dp))

                    TimerSection(
                        sessionElapsed, connectionElapsed, estimatedLatencyMs,
                        isPaused, isConnecting, textColor
                    )

                    QualityBadge(streamInfo = streamInfo, textColor = textColor)

                    Spacer(modifier = Modifier.height(16.dp))

                    TransportControls(
                        isPaused = isPaused,
                        isPlaying = isPlaying,
                        viewModel = viewModel,
                        accentColor = accentColor,
                        textColor = textColor,
                        modifier = Modifier.fillMaxWidth()
                    )

                    Spacer(modifier = Modifier.height(16.dp))

                    SettingsSection(stayConnected, bufferMs, viewModel, textColor, accentColor, compact = true)
                }
            }
        }
    }
}

@Composable
private fun OverlayCards(
    state: PlaybackState,
    station: xyz.cottongin.radio247.data.model.Station,
    isConnecting: Boolean
) {
    if (state is PlaybackState.Reconnecting) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(24.dp),
            contentAlignment = Alignment.Center
        ) {
            Card(
                modifier = Modifier.padding(32.dp),
                elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
            ) {
                Column(
                    modifier = Modifier.padding(32.dp),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    CircularProgressIndicator()
                    Spacer(modifier = Modifier.height(16.dp))
                    Text("Reconnecting\u2026 (attempt ${state.attempt})")
                }
            }
        }
    }

    if (isConnecting) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(24.dp),
            contentAlignment = Alignment.Center
        ) {
            Card(
                modifier = Modifier.padding(32.dp),
                elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
            ) {
                Column(
                    modifier = Modifier.padding(32.dp),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    CircularProgressIndicator()
                    Spacer(modifier = Modifier.height(16.dp))
                    Text("Connecting to ${station.name}\u2026")
                }
            }
        }
    }
}

private fun formatTrackInfo(metadata: IcyMetadata?): String =
    MetadataFormatter.formatTrackInfo(metadata)

private fun formatElapsed(millis: Long): String {
    if (millis <= 0) return "0s"
    val totalSeconds = millis / 1000
    val hours = totalSeconds / 3600
    val minutes = (totalSeconds % 3600) / 60
    val seconds = totalSeconds % 60
    return buildString {
        if (hours > 0) append("${hours}h ")
        if (minutes > 0 || hours > 0) append("${minutes}m ")
        append("${seconds}s")
    }.trim()
}
