package com.seven.ikar.kotlin.feature.update

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.seven.ikar.kotlin.BuildConfig
import com.seven.ikar.kotlin.app.AppContainer
import com.seven.ikar.kotlin.core.network.ServerConfig
import com.seven.ikar.kotlin.core.update.AppUpdateCheckResult
import com.seven.ikar.kotlin.core.update.AppUpdateInstallerEvent
import com.seven.ikar.kotlin.core.update.AppUpdateSubmissionResult
import com.seven.ikar.kotlin.core.update.ArgusAppManifest
import com.seven.ikar.kotlin.core.update.ArgusAppUpdateConfig
import com.seven.ikar.kotlin.core.update.AvailableIkarUpdate
import com.seven.ikar.kotlin.core.update.InstalledIkarApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

data class AppUpdateUiState(
    val currentVersionName: String,
    val currentVersionCode: Long,
    val canRequestPackageInstalls: Boolean,
    val isChecking: Boolean = false,
    val isSubmitting: Boolean = false,
    val statusTitle: String,
    val statusDetail: String,
    val availableUpdate: AvailableIkarUpdate? = null,
    val latestManifest: ArgusAppManifest? = null,
    val lastInstallerEvent: AppUpdateInstallerEvent? = null
)

class AppUpdateViewModel(private val container: AppContainer) : ViewModel() {
    private val config = ArgusAppUpdateConfig(
        baseUrl = ServerConfig.ArgusBaseUrl,
        slug = ServerConfig.AndroidAppSlug,
        platform = ServerConfig.AndroidAppPlatform,
        channel = ServerConfig.AndroidUpdateChannel
    )

    private val installedApp = InstalledIkarApp(
        packageName = BuildConfig.APPLICATION_ID,
        versionCode = BuildConfig.VERSION_CODE.toLong(),
        versionName = BuildConfig.VERSION_NAME
    )

    private val _uiState = MutableStateFlow(
        createInitialState(container.appUpdateManager.canRequestPackageInstalls())
    )
    val uiState: StateFlow<AppUpdateUiState> = _uiState.asStateFlow()

    init {
        refreshStatus()
    }

    fun refreshStatus() {
        val canRequestPackageInstalls = container.appUpdateManager.canRequestPackageInstalls()
        val currentState = _uiState.value
        val shouldReplaceStatus =
            currentState.statusTitle == initialStatusTitle(false) ||
                currentState.statusTitle == initialStatusTitle(true) ||
                currentState.statusTitle == permissionRequiredTitle ||
                currentState.statusTitle == permissionGrantedTitle

        _uiState.value = currentState.copy(
            canRequestPackageInstalls = canRequestPackageInstalls,
            lastInstallerEvent = container.appUpdateManager.loadLastInstallerEvent(),
            statusTitle = if (shouldReplaceStatus) {
                if (canRequestPackageInstalls) permissionGrantedTitle else initialStatusTitle(false)
            } else {
                currentState.statusTitle
            },
            statusDetail = if (shouldReplaceStatus) {
                if (canRequestPackageInstalls) permissionGrantedDetail else initialStatusDetail(false)
            } else {
                currentState.statusDetail
            }
        )
    }

    fun requestUnknownSourcesPermission() {
        _uiState.value = _uiState.value.copy(
            canRequestPackageInstalls = false,
            statusTitle = permissionRequiredTitle,
            statusDetail = permissionRequiredDetail
        )
    }

    fun checkForUpdates() {
        if (_uiState.value.isChecking) {
            return
        }

        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isChecking = true)
            val result = withContext(Dispatchers.IO) {
                container.appUpdateManager.checkForUpdate(config, installedApp)
            }
            when (result) {
                is AppUpdateCheckResult.NotPublished -> {
                    _uiState.value = _uiState.value.copy(
                        isChecking = false,
                        availableUpdate = null,
                        latestManifest = null,
                        statusTitle = "Проверка проведена",
                        statusDetail = result.reason
                    )
                }

                is AppUpdateCheckResult.UpToDate -> {
                    _uiState.value = _uiState.value.copy(
                        isChecking = false,
                        availableUpdate = null,
                        latestManifest = result.manifest,
                        statusTitle = "Проверка проведена",
                        statusDetail = "Обновлений нет."
                    )
                }

                is AppUpdateCheckResult.UpdateAvailable -> {
                    val manifest = result.update.manifest
                    _uiState.value = _uiState.value.copy(
                        isChecking = false,
                        availableUpdate = result.update,
                        latestManifest = manifest,
                        statusTitle = "Проверка проведена",
                        statusDetail = "Обновления есть: версия ${manifest.release.version} (${manifest.release.androidVersionCode ?: "?"})."
                    )
                }

                is AppUpdateCheckResult.Incompatible -> {
                    _uiState.value = _uiState.value.copy(
                        isChecking = false,
                        availableUpdate = null,
                        latestManifest = null,
                        statusTitle = "Проверка проведена",
                        statusDetail = result.reason
                    )
                }

                is AppUpdateCheckResult.Failed -> {
                    _uiState.value = _uiState.value.copy(
                        isChecking = false,
                        availableUpdate = null,
                        latestManifest = null,
                        statusTitle = "Проверка не выполнена",
                        statusDetail = result.reason
                    )
                }
            }
            refreshStatus()
        }
    }

    fun installAvailableUpdate() {
        val update = _uiState.value.availableUpdate ?: return
        if (_uiState.value.isSubmitting) {
            return
        }

        if (!container.appUpdateManager.canRequestPackageInstalls()) {
            requestUnknownSourcesPermission()
            return
        }

        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isSubmitting = true)
            val result = withContext(Dispatchers.IO) {
                container.appUpdateManager.downloadAndInstall(update)
            }
            when (result) {
                is AppUpdateSubmissionResult.Submitted -> {
                    _uiState.value = _uiState.value.copy(
                        isSubmitting = false,
                        statusTitle = "Запрос отправлен в Android",
                        statusDetail = result.message
                    )
                }

                is AppUpdateSubmissionResult.Rejected -> {
                    _uiState.value = _uiState.value.copy(
                        isSubmitting = false,
                        statusTitle = "Установка не запущена",
                        statusDetail = result.message
                    )
                }
            }
            refreshStatus()
        }
    }

    private fun createInitialState(canRequestPackageInstalls: Boolean): AppUpdateUiState =
        AppUpdateUiState(
            currentVersionName = BuildConfig.VERSION_NAME,
            currentVersionCode = BuildConfig.VERSION_CODE.toLong(),
            canRequestPackageInstalls = canRequestPackageInstalls,
            statusTitle = initialStatusTitle(canRequestPackageInstalls),
            statusDetail = initialStatusDetail(canRequestPackageInstalls),
            lastInstallerEvent = container.appUpdateManager.loadLastInstallerEvent()
        )

    private companion object {
        const val permissionRequiredTitle = "Нужно разрешение на установку"
        const val permissionRequiredDetail =
            "Android откроет системный экран разрешения установки APK для Икар. После разрешения нажмите «Проверить» ещё раз."
        const val permissionGrantedTitle = "Разрешение на установку включено"
        const val permissionGrantedDetail =
            "Теперь кнопка «Проверить» будет искать доступные обновления на сайте Argus."

        fun initialStatusTitle(canRequestPackageInstalls: Boolean): String =
            if (canRequestPackageInstalls) {
                "Проверка обновлений ещё не запускалась"
            } else {
                "Сначала разрешите установку APK"
            }

        fun initialStatusDetail(canRequestPackageInstalls: Boolean): String =
            if (canRequestPackageInstalls) {
                "Нажмите «Проверить», чтобы сверить установленную версию с Argus."
            } else {
                "При первом нажатии «Проверить» Android должен открыть разрешение установки приложений из неизвестных источников для Икар."
            }
    }
}
