package xyz.kusoft.argusupdater

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import java.io.File

class ArgusUpdateManager(
    private val context: Context,
    private val client: ArgusUpdateClient = ArgusUpdateClient(),
    private val installer: ArgusPackageInstaller = ArgusPackageInstaller(context),
    private val preferences: ArgusUpdatePreferences = ArgusUpdatePreferences(context)
) {
    private val updateCache = ArgusUpdateCache(
        File(context.cacheDir, ArgusUpdateCache.DIRECTORY_NAME)
    )

    init {
        updateCache.clear()
    }

    fun canRequestPackageInstalls(): Boolean =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.packageManager.canRequestPackageInstalls()
        } else {
            true
        }

    fun buildUnknownSourcesIntent(): Intent =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Intent(
                Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
                Uri.parse("package:${context.packageName}")
            )
        } else {
            Intent(Settings.ACTION_SECURITY_SETTINGS)
        }

    fun loadLastInstallerEvent(): ArgusInstallerEvent? =
        preferences.loadLastInstallerEvent()

    fun buildDownloadTargetFile(@Suppress("UNUSED_PARAMETER") update: ArgusAvailableUpdate): File =
        updateCache.prepareTarget()

    fun checkForUpdate(
        config: ArgusUpdateConfig,
        installedApp: ArgusInstalledApp
    ): ArgusUpdateCheckResult {
        return try {
            val manifest = client.fetchManifest(config)
                ?: return ArgusUpdateCheckResult.UpToDate(manifest = null)

            val manifestPackageName = manifest.androidPackageName
                ?.trim()
                ?.takeIf { it.isNotBlank() }
            if (manifestPackageName != null &&
                !manifestPackageName.equals(installedApp.packageName, ignoreCase = false)
            ) {
                return ArgusUpdateCheckResult.Incompatible(
                    "Argus отдаёт пакет $manifestPackageName, а текущее приложение имеет пакет ${installedApp.packageName}."
                )
            }

            if (!manifest.release.packageKind.equals("apk", ignoreCase = true)) {
                return ArgusUpdateCheckResult.Incompatible(
                    "Argus отдал packageKind=${manifest.release.packageKind}, а updater-kit поддерживает только APK."
                )
            }

            val versionComparison = ArgusUpdateVersionPolicy.compareReleaseToInstalled(manifest.release, installedApp)
                ?: return ArgusUpdateCheckResult.Incompatible(
                    "Argus не вернул сведения, необходимые для сравнения версий."
                )

            if (versionComparison <= 0) {
                return ArgusUpdateCheckResult.UpToDate(manifest)
            }

            ArgusUpdateCheckResult.UpdateAvailable(
                ArgusAvailableUpdate(
                    config = config,
                    installedApp = installedApp,
                    manifest = manifest
                )
            )
        } catch (error: Exception) {
            ArgusUpdateCheckResult.Failed(
                reason = error.message ?: "Не удалось проверить наличие обновления в Argus.",
                cause = error
            )
        }
    }

    @Synchronized
    fun downloadAndInstall(update: ArgusAvailableUpdate): ArgusInstallSubmissionResult {
        val targetFile = buildDownloadTargetFile(update)
        return try {
            val apkFile = client.downloadRelease(update, targetFile)
            installer.submitInstall(apkFile, update)
        } catch (error: Exception) {
            val message = error.message ?: "Не удалось скачать или запустить установку обновления."
            preferences.saveInstallerEvent(
                state = ArgusInstallerEventState.Failed,
                packageName = update.installedApp.packageName,
                releaseVersion = update.manifest.release.version,
                versionCode = update.manifest.release.resolvedVersionCode,
                detail = message
            )
            ArgusInstallSubmissionResult.Rejected(message)
        } finally {
            updateCache.clear()
        }
    }
}
