package com.fabledsword.minstrel.settings.ui

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.fabledsword.minstrel.BuildConfig
import com.fabledsword.minstrel.api.ErrorCopy
import com.fabledsword.minstrel.models.UpdateInfo
import com.fabledsword.minstrel.update.data.ApkInstaller
import com.fabledsword.minstrel.update.data.UpdateRepository
import com.fabledsword.minstrel.update.data.isVersionNewer
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

/**
 * One of three terminal states the Check-for-updates button surfaces.
 * `Idle` is the pre-check state; `Latest` means the installed build
 * matches or exceeds the server's bundled APK; `UpdateAvailable`
 * surfaces an "Install vX.Y.Z" button that downloads + launches the
 * system installer via [ApkInstaller].
 */
sealed interface UpdateCheckResult {
    data object Idle : UpdateCheckResult
    data object Latest : UpdateCheckResult
    data class UpdateAvailable(val info: UpdateInfo) : UpdateCheckResult
    data class Error(val message: String) : UpdateCheckResult
}

data class AboutUiState(
    val installedVersion: String = BuildConfig.VERSION_NAME,
    val isChecking: Boolean = false,
    val isInstalling: Boolean = false,
    val installMessage: String? = null,
    val result: UpdateCheckResult = UpdateCheckResult.Idle,
)

/**
 * Backs the About card's update controls. "Check for updates" calls
 * [UpdateRepository.getLatest], compares versus the build's
 * VERSION_NAME via [isVersionNewer], and reports the terminal state.
 * When an update is available, [install] downloads the APK via
 * [ApkInstaller] and hands it to the system installer — routing the
 * user to the "install unknown apps" settings page first when that
 * permission hasn't been granted.
 */
@HiltViewModel
class AboutCardViewModel @Inject constructor(
    private val repository: UpdateRepository,
    private val installer: ApkInstaller,
) : ViewModel() {

    private val internal = MutableStateFlow(AboutUiState())
    val state: StateFlow<AboutUiState> = internal.asStateFlow()

    fun checkForUpdates() {
        if (internal.value.isChecking) return
        viewModelScope.launch {
            internal.update { it.copy(isChecking = true, installMessage = null) }
            val installed = internal.value.installedVersion
            val result = runCatching { repository.getLatest() }
                .map { latest ->
                    if (isVersionNewer(latest.version, installed)) {
                        UpdateCheckResult.UpdateAvailable(latest)
                    } else {
                        UpdateCheckResult.Latest
                    }
                }
                .getOrElse { e -> UpdateCheckResult.Error(ErrorCopy.fromThrowable(e)) }
            internal.update { it.copy(isChecking = false, result = result) }
        }
    }

    fun install(info: UpdateInfo) {
        if (internal.value.isInstalling) return
        if (!installer.canInstall()) {
            installer.requestInstallPermission()
            internal.update {
                it.copy(installMessage = "Allow installs for Minstrel, then tap Install again.")
            }
            return
        }
        viewModelScope.launch {
            internal.update { it.copy(isInstalling = true, installMessage = null) }
            runCatching { installer.downloadApk(info.apkUrl) }
                .onSuccess { apk ->
                    installer.launchInstall(apk)
                    internal.update { it.copy(isInstalling = false) }
                }
                .onFailure { e ->
                    val why = ErrorCopy.fromThrowable(e)
                    internal.update {
                        it.copy(
                            isInstalling = false,
                            installMessage = "Couldn't download update: $why",
                        )
                    }
                }
        }
    }
}
