package com.fabledsword.minstrel.update.ui

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
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.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.composables.icons.lucide.Download
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.X
import com.fabledsword.minstrel.models.UpdateInfo
import com.fabledsword.minstrel.update.data.InstallStage
import com.fabledsword.minstrel.update.data.isBusy

/**
 * Shell-level soft banner that nudges an available update. Renders
 * nothing until [UpdateBannerController] surfaces a newer bundled APK
 * (and the user hasn't dismissed that version). Ports Flutter's
 * `UpdateBanner`: a download glyph, "Update Minstrel · {version}
 * available", an Install button (download → system installer), and a
 * dismiss X. Uses an understated surface tone, not the alarming
 * error color reserved for `VersionTooOldBanner`.
 *
 * Divergence: [ApkInstaller][com.fabledsword.minstrel.update.data.ApkInstaller]
 * exposes no byte progress, so the downloading state is an
 * indeterminate bar rather than Flutter's determinate one.
 */
@Composable
fun UpdateBanner(viewModel: UpdateBannerViewModel = hiltViewModel()) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    val info = state.info
    AnimatedVisibility(
        visible = info != null,
        enter = expandVertically() + fadeIn(),
        exit = shrinkVertically() + fadeOut(),
    ) {
        // info can flip to null during the exit animation; hold the last
        // non-null so the collapsing banner keeps its content.
        val shown = info ?: return@AnimatedVisibility
        BannerBody(
            info = shown,
            stage = state.stage,
            message = state.message,
            onInstall = { viewModel.install(shown) },
            onDismiss = { viewModel.dismiss(shown.version) },
        )
    }
}

@Composable
private fun BannerBody(
    info: UpdateInfo,
    stage: InstallStage,
    message: String?,
    onInstall: () -> Unit,
    onDismiss: () -> Unit,
) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .background(MaterialTheme.colorScheme.surfaceVariant)
            .padding(start = 16.dp, top = 8.dp, end = 4.dp, bottom = 8.dp),
    ) {
        BannerRow(info = info, stage = stage, onInstall = onInstall, onDismiss = onDismiss)
        if (stage.isBusy()) {
            LinearProgressIndicator(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 4.dp),
                color = MaterialTheme.colorScheme.primary,
            )
        }
        if (message != null) {
            Text(
                text = message,
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                modifier = Modifier.padding(top = 4.dp),
            )
        }
    }
}

@Composable
private fun BannerRow(
    info: UpdateInfo,
    stage: InstallStage,
    onInstall: () -> Unit,
    onDismiss: () -> Unit,
) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        Icon(
            imageVector = Lucide.Download,
            contentDescription = null,
            tint = MaterialTheme.colorScheme.onSurfaceVariant,
        )
        Text(
            text = if (stage == InstallStage.ERROR) {
                "Update failed"
            } else {
                "Update Minstrel · ${info.version} available"
            },
            style = MaterialTheme.typography.bodySmall,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            modifier = Modifier.weight(1f),
        )
        TextButton(onClick = onInstall, enabled = !stage.isBusy()) {
            // Downloading and installing are separate words because they're now
            // separate waits — the install half suspends on the platform, which
            // may be sitting on a confirm dialog.
            Text(
                when (stage) {
                    InstallStage.DOWNLOADING -> "Downloading…"
                    InstallStage.INSTALLING -> "Installing…"
                    else -> "Install"
                },
            )
        }
        IconButton(onClick = onDismiss) {
            Icon(
                imageVector = Lucide.X,
                contentDescription = "Dismiss",
                tint = MaterialTheme.colorScheme.onSurfaceVariant,
            )
        }
    }
}
