package com.gh.gamecenter.download

import android.app.Application
import android.view.View
import androidx.lifecycle.*
import com.gh.common.exposure.ExposureUtils
import com.gh.common.exposure.ExposureUtils.logADownloadExposureEvent
import com.gh.common.history.HistoryHelper.insertGameEntity
import com.gh.common.util.DataCollectionUtils
import com.gh.common.util.PackageInstaller.createDownloadId
import com.gh.common.util.PackageInstaller.getDownloadPathWithId
import com.gh.common.util.PackageUtils
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.base.BaseSimpleDao
import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity
import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.GsonUtils.toJson
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.eventbus.EBDownloadChanged
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.PluginLocation
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.feature.utils.PlatformUtils
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import java.util.*

class UpdatableGameViewModel(
    application: Application,
    private var mSpecialPackageName: String,
    var entrance: String
) : AndroidViewModel(application) {

    private var mRawUpdatableList = ArrayList<GameUpdateEntity>()
    private var mUpdatableItemDataList = ArrayList<UpdatableDataItem>() // 最终暴露给 View 层装饰好的数据

    private var mPackageUpdateList = ArrayList<PackageUpdate>() // 包名更新列表，包括了我的版本和其它版本 (如果存在的话)

    private var mUpdatableListLiveData = MutableLiveData<ArrayList<UpdatableDataItem>>()

    private var mHasLandPageAddressDialog = false // 是否存在由第三方提供的游戏下载

    private val mSuppressUpdateDao by lazy { SuppressUpdateDao() }
    private val mIgnoredUpdateDao by lazy { IgnoredUpdateDao() }
    private val mDownloadedGameIdAndPackageNameDao by lazy { DownloadedGameIdAndPackageNameDao() }

    // 缓存的匹配的游戏列表，列表刷新是会同时更新
    private val mCachedMatchedVersionValidUpdateList by lazy { arrayListOf<GameUpdateEntity>() }

    private val mOtherVersionExpandedMap by lazy { hashMapOf<String, Boolean>() }
    private var mShouldShowIgnoredUpdate = false

    private val mDownloadManager by lazy { DownloadManager.getInstance() }

    var updatableData: LiveData<ArrayList<UpdatableDataItem>> = mUpdatableListLiveData

    /**
     * 更新可更新数据列表
     */
    fun setUpdatableList(updatableList: List<GameUpdateEntity>) {
        if (mRawUpdatableList != updatableList) {
            mRawUpdatableList.clear()

            for (update in updatableList) {
                // 有闪退日志说这个 update 实体可能为空，实在看不原因 ：(
                if (update == null || update.isVGameUpdate) continue
                // 筛选仅下载管理出现的插件化更新
                if (update.isShowPlugin(PluginLocation.only_index)) {
                    val platform =
                        PlatformUtils.getInstance(getApplication()).getPlatformName(update.platform)
                    if (!platform.isNullOrEmpty() && "官方版" != platform) {
                        update.readablePlatform = platform
                    }

                    val downloadEntity = mDownloadManager.getDownloadEntityByUrl(update.url)
                    update.isPluggableDownloaded = downloadEntity?.isPluggable == true

                    mRawUpdatableList.add(update)
                }
            }
        }

//        mUpdatableItemDataList.clear()

        sortUpdatableListByType(mRawUpdatableList)

        mPackageUpdateList = constructPackageUpdateList(mRawUpdatableList)

        mUpdatableItemDataList = transformUpdatableListIntoUpdatableItemDataList(mPackageUpdateList)

        if (mRawUpdatableList.isNotEmpty()) {
            // TODO 父子 Fragment 共享同一个 ViewModel 来实现数据交流
            EventBus.getDefault().post(
                EBDownloadChanged(
                    "update",
                    View.VISIBLE,
                    mCachedMatchedVersionValidUpdateList.size
                )
            )
        }

        if (mSpecialPackageName.isNotEmpty()) {
            var isAlreadyExisted = false
            for (update in mRawUpdatableList) {
                if (update.packageName == mSpecialPackageName) {
                    isAlreadyExisted = true
                    break
                }
            }
            if (!isAlreadyExisted) {
                getUpdateInfoByPackageName(mSpecialPackageName)
            }
        }

        mUpdatableListLiveData.postValue(mUpdatableItemDataList)
    }

    /**
     * 构建我的版本和其它版本列表（供后续修改处理）
     */
    private fun constructPackageUpdateList(updatableList: ArrayList<GameUpdateEntity>)
            : ArrayList<PackageUpdate> {
        // 包名数组列表，因为 hashmap 在不同的系统版本上的实现不一样，在部分系统上会根据 key 的值重排序破坏列表顺序
        val packageNameList = arrayListOf<String>()
        val packageNameAndUpdateListMap = hashMapOf<String, ArrayList<GameUpdateEntity>>()
        val packageUpdateList: ArrayList<PackageUpdate> = arrayListOf()

        val idAndPackageNameRawString = mDownloadedGameIdAndPackageNameDao.getRawString()
        val permanentSuppressedUpdatePackageNameList = mSuppressUpdateDao.getAll()
        val ignoredUpdateList = mIgnoredUpdateDao.getAll()

        // 按包名分
        for (update in updatableList) {
            if (update.id == Constants.GHZS_GAME_ID) continue

            var list = packageNameAndUpdateListMap[update.packageName]
            if (list == null) {
                list = arrayListOf()
                packageNameList.add(update.packageName)
                packageNameAndUpdateListMap[update.packageName] = list
            }
            list.add(update)
        }

        // 找到我的版本
        for (packageName in packageNameList) {
            val samePackageNameUpdateList =
                packageNameAndUpdateListMap[packageName] ?: arrayListOf()
            val installedSignature =
                PackageUtils.getApkSignatureByPackageName(getApplication(), packageName)
            val isInstalledSignByGh = PackageUtils.isSignedByGh(getApplication(), packageName)
            val installedGhId = if (isInstalledSignByGh) PackageUtils.getGhId(packageName) else null
            val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName) ?: ""

            var matchedVersionUpdate: GameUpdateEntity? = null // 我的版本
            val mismatchedVersionUpdateList: ArrayList<GameUpdateEntity> = arrayListOf() // 其它版本

            // 已安装的游戏为插件游戏，找插件包里的 GH_ID 跟我的版本一样的作为我的版本
            if (isInstalledSignByGh && installedGhId != null) {
                for (update in samePackageNameUpdateList) {
                    if (update.id == installedGhId) {
                        matchedVersionUpdate = update
                    } else {
                        mismatchedVersionUpdateList.add(update)
                    }
                }
            } else if (samePackageNameUpdateList.find {
                    it.signature == installedSignature.firstOrNull()
                            || it.signature == installedSignature.secondOrNull()
                            || it.isPluggable
                } != null) {
                // 1. 存在同包名同签名的游戏，以同包名同签名的游戏作为我的版本
                // 2. 若不存在同包名同签名的游戏，以插件化的游戏作为我的版本
                for (update in samePackageNameUpdateList) {
                    if (update.isPluggable
                        || (matchedVersionUpdate == null && (installedSignature.firstOrNull() == update.signature || installedSignature.secondOrNull() == update.signature))
                    ) {
                        matchedVersionUpdate = update
                    } else {
                        mismatchedVersionUpdateList.add(update)
                    }
                }
            } else if (idAndPackageNameRawString.contains(packageName)) {
                // 光环曾经下载过对应包名的游戏，找到对应游戏ID的更新作为我的版本
                for (update in samePackageNameUpdateList) {
                    if (matchedVersionUpdate == null && idAndPackageNameRawString.contains(update.id)) {
                        matchedVersionUpdate = update
                    } else {
                        mismatchedVersionUpdateList.add(update)
                    }
                }
            } else {
                // 以同包名中七天下载量最高的游戏作为我的版本
                for (update in samePackageNameUpdateList) {
                    if (update.download >= matchedVersionUpdate?.download ?: 0) {
                        matchedVersionUpdate = update
                    }

                    mismatchedVersionUpdateList.add(update)
                }

                mismatchedVersionUpdateList.remove(matchedVersionUpdate)
                mismatchedVersionUpdateList.sortBy { it.download }
            }

            // 清理 mismatchedVersionUpdateList 的同版本数据，同版本只在找不到 matchedVersion 的时候作替补用
            val iterator = mismatchedVersionUpdateList.iterator()
            while (iterator.hasNext()) {
                val update = iterator.next()
                if (update.version == installedVersionName) {
                    iterator.remove()
                    continue
                }
            }

            // 类型不为单机游戏/gj单机的游戏或选择了临时隐藏或永久隐藏的不需要显示其它版本
            if ((matchedVersionUpdate?.category != "local" && matchedVersionUpdate?.category != "gjlocal")
                || permanentSuppressedUpdatePackageNameList?.contains(packageName) == true
                || SPUtils.getBoolean(SP_TEMPORARY_SUPPRESS_UPDATE_PREFIX + packageName + matchedVersionUpdate.currentVersion)
            ) {
                mismatchedVersionUpdateList.clear()
            }

            // 被忽略的更新不需要显示其它版本
            if (ignoredUpdateList?.contains(packageName + matchedVersionUpdate?.version) == true) {
                // 若我的版本与当前应用版本一致那么取其它版本的第一个来替换成我的版本 （避免出现忽略更新列表显示按钮为启动的问题）
//                if (packageUpdate.matchedVersionUpdate.version == PackageUtils.getVersionNameByPackageName(packageName)
//                        && packageUpdate.mismatchedVersionUpdateList.isNotEmpty()) {
//                    packageUpdate.matchedVersionUpdate = packageUpdate.mismatchedVersionUpdateList.first()
//                }
                mismatchedVersionUpdateList.clear()
            }


            // 不存在匹配版本
            // 或者匹配版本与已安装版本相同，但不是插件化没有其它可更新版本的不需要添加进列表中
            if (matchedVersionUpdate == null ||
                (matchedVersionUpdate.version == installedVersionName
                        && !PackagesManager.isCanUpdate(installedGhId as? String, packageName, false)
                        && !isUpdatePluggableRelated(matchedVersionUpdate)
                        && mismatchedVersionUpdateList.isEmpty())
            ) {
                continue
            }

            packageUpdateList.add(
                PackageUpdate(
                    matchedVersionUpdate,
                    mismatchedVersionUpdateList
                )
            )
        }
        return packageUpdateList
    }

    /**
     * 将我的版本和其它版本列表变形成列表可显示的数据
     */
    private fun transformUpdatableListIntoUpdatableItemDataList(packageUpdateList: ArrayList<PackageUpdate>)
            : ArrayList<UpdatableDataItem> {
        val updatableDataItemList = arrayListOf<UpdatableDataItem>()

        val validPackageUpdateList: ArrayList<PackageUpdate> = arrayListOf() // 按钮为更新的我的游戏列表
        val invalidPackageUpdateList: ArrayList<PackageUpdate> = arrayListOf() // 按钮为启动的我的游戏列表
        val ignoredPackageUpdateList: ArrayList<PackageUpdate> = arrayListOf() // 被隐藏的我的游戏列表
        val landPageAddressUpdateList: ArrayList<PackageUpdate> = arrayListOf() // 跳转第三方落地页的游戏列表

        val ignoredUpdateList = mIgnoredUpdateDao.getAll()

        mCachedMatchedVersionValidUpdateList.clear()

        for (packageUpdate in packageUpdateList) {
            val matchedPackageName = packageUpdate.matchedVersionUpdate.packageName
            val matchedVersionName = packageUpdate.matchedVersionUpdate.version
            val currentlyUpdatableVersion = packageUpdate.matchedVersionUpdate.currentVersion

            val isInstalledSignByGh =
                PackageUtils.isSignedByGh(getApplication(), matchedPackageName)
            val installedGhId: String? =
                if (isInstalledSignByGh) PackageUtils.getGhId(matchedPackageName) as String? else null

            // 根据状态分子列表
            // 1. 按钮为更新的我的游戏
            // 2. 按钮为启动的我的游戏
            // 3. 忽略更新的游戏
            when {
                ignoredUpdateList?.contains(matchedPackageName + matchedVersionName) == true -> {
                    ignoredPackageUpdateList.add(packageUpdate)
                }

                packageUpdate.matchedVersionUpdate.version == currentlyUpdatableVersion
                        && !PackagesManager.isCanUpdate(installedGhId, matchedPackageName, false)
                        && !isUpdatePluggableRelated(packageUpdate.matchedVersionUpdate)
                        && !packageUpdate.matchedVersionUpdate.isLandPageAddressDialog() -> {
                    invalidPackageUpdateList.add(packageUpdate)
                }

                packageUpdate.matchedVersionUpdate.isLandPageAddressDialog() -> {// 第三方落地页跳转
                    landPageAddressUpdateList.add(packageUpdate)
                }

                else -> {
                    validPackageUpdateList.add(packageUpdate)
                }
            }
        }

        mHasLandPageAddressDialog = landPageAddressUpdateList.isNotEmpty()

        // 构建装饰好的页面列表
        // 正常的我的版本
        if (validPackageUpdateList.isNotEmpty()) {
            var totalMatchedVersionValidUpdateSize = 0L
            val matchedVersionValidUpdateUrlList = arrayListOf<String>()
            val updateCount = validPackageUpdateList.size
            for (packageUpdate in validPackageUpdateList) {
                // 上一个样式为其它版本，添加白色分割线
                if (updatableDataItemList.lastOrNull()?.otherVersionUpdateHint != null
                    || updatableDataItemList.lastOrNull()?.otherVersionUpdate != null
                ) {
                    updatableDataItemList.add(UpdatableDataItem(divider = WHITE))
                }

                if (packageUpdate.mismatchedVersionUpdateList.isNotEmpty()) {
                    updatableDataItemList.add(UpdatableDataItem(normalUpdateWithArrow = packageUpdate.matchedVersionUpdate))
                    updatableDataItemList.add(
                        UpdatableDataItem(
                            otherVersionUpdateHint = mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true,
                            miscPackageName = packageUpdate.matchedVersionUpdate.packageName,
                            miscVersion = packageUpdate.matchedVersionUpdate.currentVersion ?: ""
                        )
                    )
                    if (mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true) {
                        for (update in packageUpdate.mismatchedVersionUpdateList) {
                            updatableDataItemList.add(UpdatableDataItem(otherVersionUpdate = update))
                        }
                        updatableDataItemList.add(UpdatableDataItem(divider = BLUE))
                    }
                } else {
                    updatableDataItemList.add(UpdatableDataItem(normalUpdate = packageUpdate.matchedVersionUpdate))
                }

                mCachedMatchedVersionValidUpdateList.add(packageUpdate.matchedVersionUpdate)

                totalMatchedVersionValidUpdateSize += sizeStringToLong(
                    packageUpdate.matchedVersionUpdate.size ?: "0M"
                )

                matchedVersionValidUpdateUrlList.add(packageUpdate.matchedVersionUpdate.url ?: "")
            }

            val text = getUpdateTextFromUrlList(matchedVersionValidUpdateUrlList)

            updatableDataItemList.add(
                0,
                UpdatableDataItem(
                    header = "${updateCount}个游戏可更新，共${
                        totalMatchedVersionValidUpdateSize.toProperReadableSize().replace(" ", "")
                    }",
                    miscShowUpdateAll = true,
                    miscUpdateText = text
                )
            )
            updatableDataItemList.add(0, UpdatableDataItem(divider = GREY))
        }

        // 只有其它版本的我的版本
        if (invalidPackageUpdateList.isNotEmpty()) {
            if (updatableDataItemList.size != 0) {
                updatableDataItemList.add(UpdatableDataItem(divider = WHITE))
            }
            updatableDataItemList.add(UpdatableDataItem(divider = GREY))
            updatableDataItemList.add(UpdatableDataItem(header = "以下游戏有其他版本可以更新"))
            for (packageUpdate in invalidPackageUpdateList) {
                if (packageUpdate.mismatchedVersionUpdateList.isNotEmpty()) {
                    updatableDataItemList.add(UpdatableDataItem(normalUpdateWithArrow = packageUpdate.matchedVersionUpdate))
                    updatableDataItemList.add(
                        UpdatableDataItem(
                            otherVersionUpdateHint = mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true,
                            miscPackageName = packageUpdate.matchedVersionUpdate.packageName,
                            miscVersion = packageUpdate.matchedVersionUpdate.currentVersion ?: ""
                        )
                    )
                    if (mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true) {
                        for (update in packageUpdate.mismatchedVersionUpdateList) {
                            updatableDataItemList.add(UpdatableDataItem(otherVersionUpdate = update))
                        }
                        updatableDataItemList.add(UpdatableDataItem(divider = BLUE))
                    }
                } else {
                    updatableDataItemList.add(UpdatableDataItem(normalUpdate = packageUpdate.matchedVersionUpdate))
                }
            }
            updatableDataItemList.add(UpdatableDataItem(divider = WHITE))
        }

        // 被隐藏的我的版本
        if (ignoredPackageUpdateList.isNotEmpty()) {
            updatableDataItemList.add(UpdatableDataItem(divider = WHITE))
            updatableDataItemList.add(UpdatableDataItem(divider = GREY))
            updatableDataItemList.add(UpdatableDataItem(ignoredUpdateHeader = "${ignoredPackageUpdateList.size}个游戏已忽略更新"))
            if (mShouldShowIgnoredUpdate) {
                for (packageUpdate in ignoredPackageUpdateList) {
                    updatableDataItemList.add(UpdatableDataItem(ignoredUpdate = packageUpdate.matchedVersionUpdate))
                }
            }
        }

        // 游戏下载资源由第三方提供
        if (landPageAddressUpdateList.isNotEmpty()) {
            if (updatableDataItemList.size != 0) {
                updatableDataItemList.add(UpdatableDataItem(divider = WHITE))
            }
            updatableDataItemList.add(UpdatableDataItem(landPageAddressDialogHeader = "以下游戏下载资源由第三方提供"))
            for (packageUpdate in landPageAddressUpdateList) {
                if (packageUpdate.mismatchedVersionUpdateList.isNotEmpty()) {
                    updatableDataItemList.add(UpdatableDataItem(normalUpdateWithArrow = packageUpdate.matchedVersionUpdate))
                    updatableDataItemList.add(
                        UpdatableDataItem(
                            otherVersionUpdateHint = mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true,
                            miscPackageName = packageUpdate.matchedVersionUpdate.packageName,
                            miscVersion = packageUpdate.matchedVersionUpdate.currentVersion ?: ""
                        )
                    )
                    if (mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true) {
                        for (update in packageUpdate.mismatchedVersionUpdateList) {
                            updatableDataItemList.add(UpdatableDataItem(otherVersionUpdate = update))
                        }
                        updatableDataItemList.add(UpdatableDataItem(divider = BLUE))
                    }
                } else {
                    updatableDataItemList.add(UpdatableDataItem(normalUpdate = packageUpdate.matchedVersionUpdate))
                }
            }
        }

        return updatableDataItemList
    }

    /**
     * 根据所给的 url 列表返回头部右上角的文本内容
     */
    private fun getUpdateTextFromUrlList(matchedVersionValidUpdateUrlList: ArrayList<String>)
            : String {
        var updateTaskCount = 0
        var updateCompleteTaskCount = 0
        for (url in matchedVersionValidUpdateUrlList) {
            val downloadEntity = DownloadManager.getInstance()
                .getDownloadEntityByUrl(url)
            if (downloadEntity != null) {
                updateTaskCount++
                if (downloadEntity.status == DownloadStatus.done) {
                    updateCompleteTaskCount++
                }
            }
        }

        return if (updateCompleteTaskCount == matchedVersionValidUpdateUrlList.size) {
            "更新完成"
        } else {
            if (updateTaskCount == matchedVersionValidUpdateUrlList.size) {
                "更新中"
            } else {
                "全部更新"
            }
        }
    }

    /**
     * 根据类型排序
     */
    private fun sortUpdatableListByType(list: List<GameUpdateEntity>) {
        tryCatchInRelease {
            Collections.sort(list) { lhs, rhs ->
                if ("光环助手" == rhs?.name) {
                    1
                } else if (lhs?.name?.contains("光环助手") == true) {
                    -1
                } else if (!isUpdatePluggableRelated(lhs) && isUpdatePluggableRelated(rhs)) {
                    -1
                } else if (isUpdatePluggableRelated(lhs) && !isUpdatePluggableRelated(rhs)) {
                    1
                } else {
                    0
                }
            }
        }
    }

    private fun isUpdatePluggableRelated(update: GameUpdateEntity): Boolean {
        return update.isPluggable || update.isPluggableDownloaded
    }

    private fun getUpdateInfoByPackageName(packageName: String) {
        val ghId: Any? = PackageUtils.getMetaData(getApplication(), packageName, "gh_id")
        val retrofit = RetrofitManager.getInstance().api

        if (ghId == null) {
            retrofit.loadGameDataByPackageName(UrlFilterUtils.getFilterQuery("package", packageName))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Response<List<GameEntity?>?>() {
                    override fun onResponse(response: List<GameEntity?>?) {
                        response?.let {
                            for (game in response) {
                                val simpleUpdatableList: List<GameUpdateEntity> = PackageUtils.getUpdateData(game, false)

                                if (simpleUpdatableList.isNotEmpty()) {
                                    mRawUpdatableList.addAll(simpleUpdatableList)
                                }
                            }
                        }
                        refreshList()
                    }
                })
        } else {
            retrofit.getGameUpdateById(ghId as String)
                .map(ApkActiveUtils.filterMapper)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Response<GameEntity?>() {
                    override fun onResponse(response: GameEntity?) {
                        val simpleUpdatableList: List<GameUpdateEntity> = PackageUtils.getUpdateData(response, false)

                        mRawUpdatableList.addAll(simpleUpdatableList)

                        refreshList()
                    }
                })
        }

        mSpecialPackageName = ""
    }

    fun toggleIgnoredUpdateVisibility() {
        mShouldShowIgnoredUpdate = !mShouldShowIgnoredUpdate
        setUpdatableList(mRawUpdatableList)
    }

    fun toggleOtherVersionVisibility(packageName: String) {
        mOtherVersionExpandedMap[packageName] = !(mOtherVersionExpandedMap[packageName] ?: false)
        setUpdatableList(mRawUpdatableList)
    }

    fun suppressUpdate(
        packageName: String,
        currentUpdatableVersion: String,
        suppressPermanently: Boolean
    ) {
        if (suppressPermanently) {
            mSuppressUpdateDao.add(packageName)
        } else {
            SPUtils.setBoolean(
                SP_TEMPORARY_SUPPRESS_UPDATE_PREFIX + packageName + currentUpdatableVersion,
                true
            )
        }
        setUpdatableList(mRawUpdatableList)
    }

    fun ignoreUpdate(packageName: String, versionName: String) {
        mIgnoredUpdateDao.add(packageName + versionName)
        setUpdatableList(mRawUpdatableList)
    }

    fun undoIgnoredUpdate(packageName: String, versionName: String) {
        mIgnoredUpdateDao.delete(packageName + versionName)
        setUpdatableList(mRawUpdatableList)
    }

    fun isIgnoredUpdateExpanded(): Boolean {
        return mShouldShowIgnoredUpdate
    }

    /**
     * 全部更新 (版本匹配的我的版本)
     */
    fun updateAllMatchedVersionValidUpdate() {
        val updateList = ArrayList(mCachedMatchedVersionValidUpdateList)
        for (update in updateList) {
            update(update, false)
        }
    }

    /**
     * 是否存在第三方提供下载的游戏
     */
    fun hasLandPageAddressDialog(): Boolean = mHasLandPageAddressDialog

    /**
     * 更新列表状态
     */
    fun refreshList() {
        setUpdatableList(mRawUpdatableList)
    }

    /**
     * 执行单个更新
     * @param update 更新内容
     * @param isSubscribe 是否延迟到连上 WIFI 才下载
     */
    fun update(update: GameUpdateEntity, isSubscribe: Boolean) {
        val downloadType: ExposureUtils.DownloadType
        val downloadId = createDownloadId(update.name)

        val downloadEntity = DownloadEntity()
        downloadEntity.gameId = update.id
        downloadEntity.url = update.url
        downloadEntity.name = update.name
        downloadEntity.path = getDownloadPathWithId(downloadId, update.format)
        downloadEntity.eTag = update.etag
        downloadEntity.icon = update.icon
        downloadEntity.platform = update.platform
        downloadEntity.packageName = update.packageName
        downloadEntity.versionName = update.version
        downloadEntity.pluginDesc = update.pluginDesc
        downloadEntity.addMetaExtra(Constants.RAW_GAME_ICON, update.rawIcon)
        downloadEntity.addMetaExtra(Constants.GAME_ICON_SUBSCRIPT, update.iconSubscript)
        downloadEntity.addMetaExtra(Constants.DOWNLOAD_ID, downloadId)
        downloadEntity.addMetaExtra(Constants.APK_MD5, update.md5)
        downloadEntity.addMetaExtra(Constants.GAME_NAME, update.name)
        downloadEntity.addMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE, update.categoryChinese)
        downloadEntity.addMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE, update.downloadStatusChinese)
        downloadEntity.putGameCategory(update.category ?: "")
        if (update.isModdedGame) {
            downloadEntity.addMetaExtra(Constants.EXTRA_IS_MODDED_GAME, "true")
        }
        if (update.iconFloat != null) {
            downloadEntity.addMetaExtra(Constants.GAME_ICON_FLOAT_TOP_TEXT, update.iconFloat?.upperLeftText)
            downloadEntity.addMetaExtra(Constants.GAME_ICON_FLOAT_TOP_COLOR, update.iconFloat?.upperLeftColor)
            downloadEntity.addMetaExtra(Constants.GAME_ICON_FLOAT_BOTTOM_TEXT, update.iconFloat?.bottomText)
        }

        val platform = PlatformUtils.getInstance(getApplication()).getPlatformName(update.platform)
        if ("官方版" != platform) {
            downloadEntity.isPlugin = true
        }
        if (update.isPluggable) {
            downloadEntity.isPluggable = true
        } else {
            downloadEntity.isUpdate = true
        }

        // 确定下载类型
        downloadType = when {
            downloadEntity.isPluggable -> ExposureUtils.DownloadType.PLUGIN_DOWNLOAD
            downloadEntity.isPlugin -> ExposureUtils.DownloadType.PLUGIN_UPDATE
            else -> ExposureUtils.DownloadType.UPDATE
        }

        val gameEntity = GameEntity(update.id, update.name)
        gameEntity.gameVersion = update.version ?: ""

        val event = logADownloadExposureEvent(
            gameEntity,
            update.platform,
            update.exposureEvent,
            downloadType
        )

        downloadEntity.exposureTrace = toJson(event)
        downloadEntity.entrance = "$entrance+(下载管理:游戏更新)"
        downloadEntity.location = "游戏更新:列表"
        // 保存所有游戏标签
        val tags = update.tagStyle.map { it.name }.also { downloadEntity.tags = it }

        if (update.name?.contains("光环助手") == true) {
            DownloadManager.getInstance().pauseAll()
        }
        if (isSubscribe) {
            DownloadManager.getInstance().subscribe(downloadEntity)
        } else {
            insertGameEntity(update)
            DownloadManager.getInstance().add(downloadEntity)
        }

        // 收集下载数据
        DataCollectionUtils.uploadDownload(getApplication(), downloadEntity, "开始")

        val pushMessageId = (HaloApp.get(Constants.PUSH_MESSAGE_ID, false) as? String) ?: ""
        val pushLinkId = (HaloApp.get(Constants.PUSH_LINK_ENTITY, false) as? LinkEntity)?.link ?: ""
        val isFromPush = pushMessageId.isNotEmpty()
        SensorsBridge.trackEventWithExposureSource(
            "DownloadProcessBegin",
            event.source,
            "game_id", update.id,
            "game_name", update.name ?: "",
            "game_type", update.categoryChinese,
            "game_label", tags.joinToString(","),
            "page_name", getCurrentPageEntity().pageName,
            "page_id", getCurrentPageEntity().pageId,
            "page_business_id", getCurrentPageEntity().pageBusinessId,
            "last_page_name", getLastPageEntity().pageName,
            "last_page_id", getLastPageEntity().pageId,
            "last_page_business_id", getLastPageEntity().pageBusinessId,
            "download_status", update.downloadStatusChinese,
            "download_type", "本地下载",
            "is_from_push_notifications", isFromPush,
            "message_id", pushMessageId,
            "link_id", pushLinkId,
        )
    }

    private fun sizeStringToLong(sizeString: String): Long {
        val lastM = sizeString.lastIndexOf("M")
        if (lastM != -1) {
            return (sizeString.substring(0, lastM).toFloat() * 1024 * 1024).toLong()
        }
        return 0L
    }

    companion object {
        const val SP_TEMPORARY_SUPPRESS_UPDATE_PREFIX = "temporary_suppressed_update_prefix_"

        const val WHITE = "white"
        const val GREY = "grey"
        const val BLUE = "blue"
    }

    /**
     * - 分割线(包括灰色,白色和浅蓝色)
     * - 信息头(包括更新信息和普通文本)
     * - 忽略更新信息头
     * - 普通游戏
     * - 含小箭头的普通游戏
     * - 其它版本游戏
     * - 其它版本游戏提示(包括展开和收起状态)
     *
     * - 其它信息(包名)
     */
    class UpdatableDataItem(
        var divider: String? = null,
        var header: String? = null,
        var ignoredUpdateHeader: String? = null,
        var landPageAddressDialogHeader: String? = null,
        var normalUpdate: GameUpdateEntity? = null,
        var normalUpdateWithArrow: GameUpdateEntity? = null,
        var ignoredUpdate: GameUpdateEntity? = null,
        var otherVersionUpdate: GameUpdateEntity? = null,
        var otherVersionUpdateHint: Boolean? = null,

        // 其它信息，是上面单独 item 信息的补充
        var miscPackageName: String = "", // 包名
        var miscVersion: String = "", // 版本
        var miscShowUpdateAll: Boolean = false, // 显示更新全部按钮
        var miscUpdateText: String = "" // 更新按钮的文案 （有`全部更新`,`更新中`,`已更新`三种）
    )

    private class PackageUpdate(
        var matchedVersionUpdate: GameUpdateEntity, // AKA 我的版本
        var mismatchedVersionUpdateList: ArrayList<GameUpdateEntity>
    )

    private class SuppressUpdateDao : BaseSimpleDao() {
        override fun getSPKey(): String = "suppressed_update"
    }

    private class IgnoredUpdateDao : BaseSimpleDao() {
        override fun getSPKey(): String = "ignored_update"
    }

    class Factory(private var mSpecialPackageName: String, private var mEntrance: String) :
        ViewModelProvider.NewInstanceFactory() {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return UpdatableGameViewModel(
                HaloApp.getInstance().application,
                mSpecialPackageName,
                mEntrance
            ) as T
        }
    }

}