package hanson.xyz.vpnhotspotmod.util

import android.app.Activity
import android.net.Uri
import hanson.xyz.vpnhotspotmod.App.Companion.app
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.install.InstallException
import com.google.android.play.core.install.model.InstallErrorCode
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.ktx.AppUpdateResult
import com.google.android.play.core.ktx.requestUpdateFlow
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber

object UpdateChecker {
    private class UpdateAvailable(private val update: AppUpdateResult.Available) : AppUpdate {
        override val stalenessDays get() = update.updateInfo.clientVersionStalenessDays() ?: 0
        override fun updateForResult(activity: Activity, requestCode: Int) = try {
            check(update.startFlexibleUpdate(activity, requestCode)) { "startFlexibleUpdate failed" }
        } catch (e: Exception) {
            Timber.w(e)
            app.customTabsIntent.launchUrl(activity,
                Uri.parse("https://play.google.com/store/apps/details?id=hanson.xyz.vpnhotspotmod"))
        }
    }
    private class UpdateDownloading(private val update: AppUpdateResult.InProgress) : AppUpdate {
        override val downloaded get() = false
        override val message: String? get() {
            if (update.installState.installStatus() != InstallStatus.FAILED) return null
            val code = update.installState.installErrorCode()
            for (f in InstallErrorCode::class.java.declaredFields) if (f.getInt(null) == code) return f.name
            return "Unrecognized Error"
        }
    }
    private class UpdateDownloaded(private val update: AppUpdateResult.Downloaded) : AppUpdate {
        override val downloaded get() = true
        override val stalenessDays get() = 0
        override fun updateForResult(activity: Activity, requestCode: Int) {
            GlobalScope.launch { update.completeUpdate() }
        }
    }

    private val manager by lazy { AppUpdateManagerFactory.create(app) }

    fun check() = manager.requestUpdateFlow().catch { e ->
        when (e) {
            is InstallException -> {
                app.logEvent("InstallErrorCode") { param("errorCode", e.errorCode.toLong()) }
                throw AppUpdate.IgnoredException(e)
            }
            is RuntimeException -> if (e.message == "Failed to bind to the service.") {
                app.logEvent("UpdateBindFailure")
                throw AppUpdate.IgnoredException(e)
            }
        }
        throw e
    }.map { result ->
        when (result) {
            is AppUpdateResult.NotAvailable -> null
            is AppUpdateResult.Available -> UpdateAvailable(result)
            is AppUpdateResult.InProgress -> {
                if (result.installState.installStatus() == InstallStatus.CANCELED) null else UpdateDownloading(result)
            }
            is AppUpdateResult.Downloaded -> UpdateDownloaded(result)
        }
    }
}
