package com.adealink.weparty.update

import androidx.fragment.app.FragmentActivity
import com.adealink.frame.aab.util.getCompatString
import com.adealink.frame.frame.BaseFrame
import com.adealink.frame.log.Log
import com.adealink.frame.util.AppUtil
import com.adealink.frame.util.PackageUtil
import com.adealink.frame.util.differentDays
import com.adealink.weparty.R
import com.adealink.weparty.commonui.widget.CommonDialog
import com.adealink.weparty.storage.AppPref
import com.adealink.weparty.update.listener.IUpdateListener
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
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
import java.lang.Long.max
import java.lang.ref.WeakReference
import java.util.Date
import kotlin.math.min

val updateManager: IUpdateManager by lazy { UpdateManager() }

class UpdateManager : BaseFrame<IUpdateListener>(), IUpdateManager {

    private var hasNewVersion = false
    private val appUpdateManager by lazy { AppUpdateManagerFactory.create(AppUtil.appContext) }
    private var activityWeakReference: WeakReference<FragmentActivity>? = null
    private val forceUpdateVersionCodeSet = mutableSetOf<Int>()
    private val installStateUpdatedListener by lazy {
        InstallStateUpdatedListener { state ->
            if (state.installStatus() == InstallStatus.DOWNLOADED) {
                showCompleteUpdateDialog()
            }
        }
    }

    override fun hasNewVersionUpdate(): Boolean {
        return hasNewVersion
    }


    override fun checkUpdate(activity: FragmentActivity) {
        activityWeakReference = WeakReference(activity)
        appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
            Log.i(
                TAG_UPDATE,
                "check update success, availableVersionCode:${appUpdateInfo.availableVersionCode()}"
            )
            val installStatus = appUpdateInfo.installStatus()
            val updateAvailability = appUpdateInfo.updateAvailability()
            if (installStatus == InstallStatus.DOWNLOADED
                || updateAvailability == UpdateAvailability.UPDATE_AVAILABLE
            ) {
                hasNewVersion = true
                dispatch {
                    it.notifyHasNewVersion(true)
                }
            }

            if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                showCompleteUpdateDialog()
                return@addOnSuccessListener
            }
            if (appUpdateInfo.updateAvailability()
                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
            ) {
                startUpdate(appUpdateInfo, AppUpdateType.IMMEDIATE)
                return@addOnSuccessListener
            }
            if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
                if (shouldUpdateImmediate()) {
                    startUpdate(appUpdateInfo, AppUpdateType.IMMEDIATE)
                    return@addOnSuccessListener
                }
                if (appUpdateInfo.updatePriority() == PRIORITY_FORE_UPDATE && appUpdateInfo.isUpdateTypeAllowed(
                        AppUpdateType.IMMEDIATE
                    )
                ) {
                    Log.d(TAG_UPDATE, "immediate update checked")
                    //立即更新
                    startUpdate(appUpdateInfo, AppUpdateType.IMMEDIATE)
                    return@addOnSuccessListener
                }
                val stalenessDay = appUpdateInfo.clientVersionStalenessDays() ?: 0
                if (stalenessDay >= DAYS_FOR_FLEXIBLE_UPDATE && appUpdateInfo.isUpdateTypeAllowed(
                        AppUpdateType.FLEXIBLE
                    )
                ) {
                    Log.d(TAG_UPDATE, "flex update checked")
                    //灵活更新, 7天一次
                    val lastCheckFlexUpdateTime = AppPref.startFlexUpdateTime
                    val nowTimeMs = System.currentTimeMillis()
                    val days = differentDays(
                        Date(min(lastCheckFlexUpdateTime, nowTimeMs)),
                        Date(max(lastCheckFlexUpdateTime, nowTimeMs))
                    )
                    if (days < 7) {
                        return@addOnSuccessListener
                    }
                    AppPref.startFlexUpdateTime = System.currentTimeMillis()
                    Log.d(TAG_UPDATE, "start flex update")
                    startUpdate(appUpdateInfo, AppUpdateType.FLEXIBLE)
                }
            }
        }.addOnFailureListener {
            Log.i(
                TAG_UPDATE,
                "check update failed, error:${it}"
            )
        }
        appUpdateManager.registerListener(installStateUpdatedListener)
    }

    private fun startUpdate(appUpdateInfo: AppUpdateInfo, appUpdateType: Int) {
        val checkActivity = activityWeakReference?.get() ?: return
        try {
            appUpdateManager.startUpdateFlowForResult(
                appUpdateInfo,
                appUpdateType,
                checkActivity,
                REQUEST_CODE_UPDATE
            )
        } catch (e: Exception) {
            Log.e(TAG_UPDATE, "startUpdate exp:$e")
        }
    }

    private fun showCompleteUpdateDialog() {
        val checkActivity = (AppUtil.currentActivity as? FragmentActivity) ?: return
        CommonDialog.Builder()
            .message(getCompatString(R.string.common_update_downloaded))
            .positiveText(getCompatString(R.string.common_restart))
            .onPositive {
                appUpdateManager.completeUpdate()
            }
            .setShowDefaultCancel(true)
            .canceledOnTouchOutside(false)
            .build()
            .show(checkActivity.supportFragmentManager)
    }

    private fun shouldUpdateImmediate(): Boolean {
        if (forceUpdateVersionCodeSet.isEmpty()) {
            return false
        }
        val currentVersionCode = PackageUtil.getVersionCode()
        forceUpdateVersionCodeSet.forEach { versionCode ->
            if (versionCode > currentVersionCode) {
                return true
            }
        }
        return false
    }
}