package pl.firmatpp.kierowca.update

import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.appupdate.AppUpdateOptions
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability

data class PlayAppUpdateState(
    val updateAvailable: Boolean = false,
    val availableVersionCode: Int? = null,
    val downloaded: Boolean = false,
    val checking: Boolean = false,
)

class PlayInAppUpdateController(
    private val activity: ComponentActivity,
    private val launcher: ActivityResultLauncher<IntentSenderRequest>,
    private val onStateChanged: (PlayAppUpdateState) -> Unit,
    private val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(activity),
) {
    private var state = PlayAppUpdateState()
    private val installStateListener = InstallStateUpdatedListener { installState ->
        when (installState.installStatus()) {
            InstallStatus.DOWNLOADED -> updateState(state.copy(downloaded = true, updateAvailable = true))
            InstallStatus.INSTALLED -> updateState(PlayAppUpdateState())
            else -> Unit
        }
    }

    init {
        appUpdateManager.registerListener(installStateListener)
    }

    fun checkForUpdate() {
        updateState(state.copy(checking = true))
        appUpdateManager.appUpdateInfo
            .addOnSuccessListener(::handleUpdateInfo)
            .addOnFailureListener { updateState(state.copy(checking = false)) }
    }

    fun startUpdate(required: Boolean, playStoreUrl: String?) {
        appUpdateManager.appUpdateInfo
            .addOnSuccessListener { info ->
                if (info.updateAvailability() != UpdateAvailability.UPDATE_AVAILABLE) {
                    openPlayStore(playStoreUrl)
                    return@addOnSuccessListener
                }

                val preferredType = if (required) AppUpdateType.IMMEDIATE else AppUpdateType.FLEXIBLE
                val updateType = when {
                    info.isUpdateTypeAllowed(preferredType) -> preferredType
                    info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) -> AppUpdateType.FLEXIBLE
                    info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) -> AppUpdateType.IMMEDIATE
                    else -> null
                }

                if (updateType == null || !launchUpdate(info, updateType)) {
                    openPlayStore(playStoreUrl)
                }
            }
            .addOnFailureListener { openPlayStore(playStoreUrl) }
    }

    fun completeDownloadedUpdate() {
        appUpdateManager.completeUpdate()
    }

    fun onUpdateFlowResult(resultCode: Int) {
        if (resultCode != Activity.RESULT_OK) {
            checkForUpdate()
        }
    }

    fun dispose() {
        appUpdateManager.unregisterListener(installStateListener)
    }

    private fun handleUpdateInfo(info: AppUpdateInfo) {
        val downloaded = info.installStatus() == InstallStatus.DOWNLOADED
        val available = info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE ||
            info.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS ||
            downloaded

        updateState(
            PlayAppUpdateState(
                updateAvailable = available,
                availableVersionCode = info.availableVersionCode().takeIf { it > 0 },
                downloaded = downloaded,
                checking = false,
            ),
        )

        if (
            info.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS &&
            info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
        ) {
            launchUpdate(info, AppUpdateType.IMMEDIATE)
        }
    }

    private fun launchUpdate(info: AppUpdateInfo, updateType: Int): Boolean =
        appUpdateManager.startUpdateFlowForResult(
            info,
            launcher,
            AppUpdateOptions.newBuilder(updateType).build(),
        )

    private fun openPlayStore(playStoreUrl: String?) {
        val packageName = activity.packageName
        val candidates = listOf(
            Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName")).setPackage("com.android.vending"),
            Intent(
                Intent.ACTION_VIEW,
                Uri.parse(playStoreUrl?.takeIf { it.isNotBlank() }
                    ?: "https://play.google.com/store/apps/details?id=$packageName"),
            ),
        )

        for (intent in candidates) {
            try {
                activity.startActivity(intent)
                return
            } catch (_: ActivityNotFoundException) {
                // Try the browser fallback.
            } catch (_: SecurityException) {
                // Try the browser fallback.
            }
        }
    }

    private fun updateState(next: PlayAppUpdateState) {
        state = next
        onStateChanged(next)
    }
}
