package com.gh.vspace

import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.sqlite.SQLiteFullException
import android.net.Uri
import android.os.Build
import android.text.TextUtils
import android.view.View
import androidx.annotation.WorkerThread
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.constant.Config
import com.gh.common.exposure.ExposureUtils
import com.gh.common.history.HistoryHelper
import com.gh.common.util.DataCollectionUtils
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.PackageUtils
import com.gh.download.DownloadManager
import com.gh.download.PackageObserver
import com.gh.gamecenter.R
import com.gh.gamecenter.SplashScreenActivity
import com.gh.gamecenter.common.BuildConfig
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.db.VGameDatabase
import com.gh.vspace.db.VGameEntity
import com.halo.assistant.HaloApp
import com.lg.vspace.VirtualAppManager
import com.lg.vspace.remote.BinderPool
import com.lg.vspace.remote.listener.RemoteConnectListener
import com.lg.vspace.remote.model.AppInstallerInfo
import com.lg.vspace.remote.model.VGameInstallerResult
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.AppManager
import com.lightgame.utils.Utils
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.util.*

object VHelper {

    // 畅玩游戏是否已被使用过
    private const val KEY_V_IS_USED = "v_is_used"
    private const val KEY_LAST_PLAYED_TIME = "last_played_time"
    private const val KEY_LAST_ALERT_UPDATE_URL = "last_alert_update_url"
    private const val KEY_TOTAL_PLAYED_TIME = "total_played_time"

    const val LOG_TAG = "VSPACE"
    const val DEFAULT_VSPACE_PACKAGENAME = "com.lg.vspace"
    const val PLATFORM_V = "畅玩版"

    private val mDelegateManager by lazy { VirtualAppManager.get() }
    private val mPackageInstalledLiveData by lazy { MutableLiveData<String>() }
    private var mInstalledInfoList: List<AppInstallerInfo> = arrayListOf()
    private var mLastSuccessfullyLaunchedGame: Pair<Long, String>? = null

    private val mVGameDao by lazy { VGameDatabase.instance.vGameDao() }
    private var mVGameSnapshotList = arrayListOf<VGameEntity>()

    private var mUpdateEntity: AppEntity? = null

    private var mIsInitialized = false // 是否已初始化
    private var mIsServiceConnected = false // AIDL 服务是否成功连接，不可作为 AIDL 仍然可用的依据
    private var mShouldLaunchGameAfterInstallation = true // 是否需要在安装完成后启动游戏的开关

    // 当前正卡在安装中的 VA 游戏，避免重复调用安装
    private val mInstallingVaPathSet by lazy { Collections.synchronizedSet(hashSetOf<String>()) }

    // 畅玩服务连接成功前挂起的动作，连接成功后执行
    private var mPendingAction: (() -> Unit)? = null

    val vGameLiveData by lazy { mVGameDao.getAllLiveData() }

    private val mPackageObserver by lazy {
        PackageObserver.PackageChangeListener {
            val vaConfig = Config.getVSettingEntity()?.va ?: return@PackageChangeListener
            val isVSpace =
                it.packageName == vaConfig.arch32?.packageName || it.packageName == vaConfig.arch64?.packageName

            if (!isVSpace || !isVIsUsed()) return@PackageChangeListener

            if (it.type == "安装") {
                // 即时调用大概率调不起来，我也不知道为什么，只能延迟大法了
                AppExecutor.uiExecutor.executeWithDelay({
                    connectService()
                }, 500)
            } else if (it.type == "卸载") {
                // 执行卸载逻辑
            }
        }
    }

    /**
     * 初始化
     * @param observeDatabaseChanges 是否启用数据库变动监听
     */
    @SuppressLint("CheckResult")
    @JvmStatic
    fun init(context: Context, observeDatabaseChanges: Boolean) {
        if (isVGameOn()) {
            if (!mIsInitialized) {
                mIsInitialized = true

                VArchiveHelper.init()

                val config = Config.getVSettingEntity()?.va
                if (config?.arch64 != null && PackageUtils.isInstalled(context, config.arch64.packageName)) {
                    if (isVIsUsed()) {
                        connectService(true)
                    }

                    // 检查畅玩助手组件更新
                    getVSpaceUpdate(config.arch64)
                }
                PackageObserver.registerPackageChangeChangeListener(mPackageObserver)
            }

            if (observeDatabaseChanges) {
                vGameLiveData.observeForever {
                    mVGameSnapshotList = ArrayList(it)
                }
            }
        }
    }

    /**
     * 连接服务
     */
    fun connectService(shouldCheckUpdate: Boolean = false, callbackClosure: (() -> Unit)? = null) {
        toastIfServiceConnectTimeout()

        mPendingAction = callbackClosure
        mDelegateManager.connectService(object : RemoteConnectListener {
            override fun onServiceConnectionSuccessed() {
                onConnectionCompleted(shouldCheckUpdate)
            }

            override fun onServiceConnectionFailed(failCode: Int) {
                Utils.log(LOG_TAG, "V 服务连接失败")
                if (failCode == BinderPool.CONNECT_STATE_NOT_INSTALLED) {
                    Utils.log(LOG_TAG, "未安装畅玩助手")
                }
            }
        })
    }

    fun onConnectionCompleted(
        shouldReconnect: Boolean = false,
        shouldCheckUpdate: Boolean = false) {
        if (shouldReconnect) {
            connectService(false, mPendingAction)
        } else {
            Utils.log(LOG_TAG, "V 服务连接成功")

            mIsServiceConnected = true
            SPUtils.setBoolean(KEY_V_IS_USED, true)

            runOnIoThread {
                mInstalledInfoList = getInstalledList()
                mPendingAction?.invoke()
                mPendingAction = null
            }

            if (shouldCheckUpdate) {
                checkUpdateViaPackageRepository()
            }
        }
    }

    private fun toastIfServiceConnectTimeout(timeout: Long = 3000L) {
        if (!mIsServiceConnected) {
            AppExecutor.uiExecutor.executeWithDelay({
                if (!mIsServiceConnected) {
                    ToastUtils.toast("允许启动畅玩助手才能继续游戏哦~ ")
                }
            }, timeout)
        }
    }

    /**
     * 畅玩助手是否已安装
     */
    fun isVSpaceInstalled(context: Context): Boolean {
        val config = Config.getVSettingEntity()?.va
        return config?.arch64 != null && PackageUtils.isInstalled(context, config.arch64.packageName)
    }

    /**
     * 显示下载畅玩空间弹窗
     */
    fun showVspaceDialog(context: Context) {
        VSpaceDialogFragment.showDownloadDialog(context, getVSpaceDownloadEntity(false))
    }

    /**
     * 获取数据库里所有的畅玩游戏
     *
     * (查询数据库，请在工作线程调用)
     */
    @WorkerThread
    fun getAllVGame(): List<VGameEntity> = mVGameDao.getAll()

    /**
     * 获取库里全部畅玩游戏列表的内存快照
     */
    fun getAllVGameSnapshots(): ArrayList<VGameEntity> = ArrayList(mVGameSnapshotList)

    /**
     * 获取内存里的畅玩游戏快照
     *
     * @param gameId 游戏 ID
     * @param packageName 需要获取的畅玩游戏的包名
     *
     */
    @JvmStatic
    fun getVGameSnapshot(gameId: String? = null, packageName: String? = null): VGameEntity? {
        return mVGameSnapshotList.find {
            it.packageName == packageName && (gameId == null || it.downloadEntity.gameId == gameId)
        }
    }

    /**
     * 更新已安装的畅玩列表
     */
    private fun updateInstalledList() {
        mInstalledInfoList = getInstalledList()
    }

    /**
     * 是否已安装此包名的畅玩游戏
     */
    @JvmStatic
    fun isInstalled(packageName: String?): Boolean {
        Utils.log(LOG_TAG, "检查 $packageName 是否在已安装列表里")
        return mInstalledInfoList.any { it.packageName == packageName }
    }

    /**
     * 启动成功，五秒内退出才显示反馈弹框
     */
    fun showFeedbackDialogIfLastSuccessfulLaunchedGameExitUnexpectedly(activity: AppCompatActivity) {
        if (isVGameOn()) {
            val time = mLastSuccessfullyLaunchedGame?.first
            val packageName = mLastSuccessfullyLaunchedGame?.second
            if (activity !is SplashScreenActivity
                && time != null
                && packageName != null
                && System.currentTimeMillis() - time < 5000
                && !VFeedbackSuppressedSimpleDao().contains(packageName)
            ) {
                getVGameSnapshot(null, packageName)?.let {
                    VFeedbackDialogFragment.show(activity, toGameEntity(it.downloadEntity))
                    mLastSuccessfullyLaunchedGame = null
                }
            }
        }
    }

    /**
     * 获取游戏占用的空间
     */
    fun getAppOccupiedSpace(packageName: String): Long {
        return try {
            mDelegateManager.getAppOccupiedSpace(packageName)
        } catch (e: Exception) {
            e.printStackTrace()
            0
        }
    }

    /**
     * 在执行 callback 前先检查组件是否已安装，是否可下载
     */
    fun validateVSpaceBeforeAction(context: Context, gameEntity: GameEntity?, callback: () -> Unit) {
        // 仅下载类型为畅玩的类型才执行判断
        if (gameEntity == null || gameEntity.isVGame()) {
            if (showDialogIfVSpaceIsNeeded(context)) return

            val vaConfig64 = Config.getVSettingEntity()?.va?.arch64
            val installedSpaceVersionCode = PackageUtils.getVersionCodeByPackageName(vaConfig64?.packageName)

            // 检查更新
            val containsUpdate = shouldShowVSpaceUpdate(mUpdateEntity, installedSpaceVersionCode)

            if (containsUpdate) {
                val dialogType = if (mUpdateEntity!!.isAlertEveryTime()) "强制更新" else "提示更新"
                NewFlatLogUtils.logHaloFunEvent("halo_fun_update_dialog_show")
                SPUtils.setString(KEY_LAST_ALERT_UPDATE_URL, mUpdateEntity!!.url + mUpdateEntity!!.alert)
                DialogHelper.showDialog(
                    context = context,
                    title = "服务工具更新提示",
                    content = mUpdateEntity!!.content.toString(),
                    cancelText = "立即更新",
                    confirmText = "继续游戏",
                    cancelClickCallback = {
                        NewFlatLogUtils.logHaloFunUpdateDialogClick(dialogType, "立即更新")
                        VSpaceDialogFragment.showDownloadDialog(
                            context,
                            getVSpaceDownloadEntity(true),
                            autoDownload = true,
                            isUpdate = true
                        )
                    },
                    confirmClickCallback = {
                        NewFlatLogUtils.logHaloFunUpdateDialogClick(dialogType, "继续游戏")
                        callback.invoke()
                    },
                    extraConfig = DialogHelper.Config(centerTitle = true),
                    uiModificationCallback = {
                        if (mUpdateEntity!!.isAlertEveryTime()) {
                            it.confirmTv.visibility = View.GONE
                            it.cancelTv.setTextColor(R.color.theme_font.toColor(context))
                        }

                        it.confirmTv.setTextColor(R.color.text_subtitle.toColor(context))
                    }
                )
                return
            }

            callback.invoke()
        } else {
            callback.invoke()
        }
    }

    /**
     * 获取已安装的包名列表
     */
    private fun getInstalledPackageList(): ArrayList<String> {
        val installedList = mInstalledInfoList
        val installedPackageList = arrayListOf<String>()

        for (info in installedList) {
            info.packageName?.let {
                installedPackageList.add(info.packageName)
            }
        }

        Utils.log(LOG_TAG, "获取已安装包名列表 $installedPackageList")

        return installedPackageList
    }

    /**
     * 获取已安装列表信息 (每次调用都会生成一份新的副本，放心使用)
     */
    @WorkerThread
    fun getInstalledList(): ArrayList<AppInstallerInfo> {
        var list: List<AppInstallerInfo> = arrayListOf()

        try {
            list = mDelegateManager.installedGamesInfo

            Utils.log(LOG_TAG, "已安装应用数量${list.size}")
        } catch (e: Exception) {
            Utils.log(LOG_TAG, "获取已安装应用数量异常 ${e.localizedMessage}")
        }

        return ArrayList(list)
    }

    /**
     * 安装新应用
     */
    fun install(
        context: Context,
        downloadEntity: DownloadEntity,
        isManualInstall: Boolean = false
    ) {
        Utils.log(LOG_TAG, "尝试安装新应用 ${downloadEntity.path}")

        if (showDialogIfVSpaceIsNeeded(context)) return

        // 服务未能连接时或手动安装时，有条件触发安装完成即启动，但主要还是得看开关
        val shouldLaunchGameAfterInstallation = mShouldLaunchGameAfterInstallation && (!mIsServiceConnected || isManualInstall)

        // 恢复安装完启动游戏开关
        mShouldLaunchGameAfterInstallation = true

        connectService {
            val installClosure: () -> Unit = {
                if (shouldLaunchGameAfterInstallation) {
                    val gameEntity = toGameEntity(downloadEntity)
                    context.startActivity(VSpaceLoadingActivity.getIntent(context, gameEntity, true))
                }

                val path = downloadEntity.path

                // 正在安装中，忽略重复调用
                if (!mInstallingVaPathSet.contains(path)) {
                    try {
                        mInstallingVaPathSet.add(path)

                        val intent = VirtualAppManager.getInstallIntent(context, path, downloadEntity.packageName)

                        Utils.log(LOG_TAG, "安装 ${downloadEntity.packageName}")
                        context.startActivity(intent)
                    } catch (e: Exception) {
                        ToastUtils.toast(e.localizedMessage ?: "")
                    }
                }
            }

            if (mDelegateManager.isConnectAidlInterface) {
                installClosure.invoke()
            } else {
                connectService(false, installClosure)
            }
        }
    }

    /**
     * 禁用安装完直接启动游戏 (仅此次安装)
     */
    fun disableLaunchGameAfterInstallation() {
        mShouldLaunchGameAfterInstallation = false
    }

    /**
     * 安装完是否直接启动游戏
     */
    fun shouldLaunchGameAfterInstallation() = mShouldLaunchGameAfterInstallation

    fun onInstallFinished(packageName: String, result: VGameInstallerResult) {
        runOnIoThread {
            val downloadEntity = getDownloadEntityByPackageName(packageName) ?: return@runOnIoThread

            // 去掉更新标记
            downloadEntity.isUpdate = false
            mVGameDao.insert(VGameEntity.from(downloadEntity))

            if (result.status == 0) {
                updateInstalledList()
                PackageObserver.onPackageChanged(
                    EBPackage(EBPackage.TYPE_INSTALLED, result.packageName, "unknown").also {
                        it.gameId = downloadEntity.gameId
                    }
                )
                insertInstalledGameToProvider(downloadEntity)
            } else {
                ToastUtils.toast("安装出现异常， ${result.status}")
            }

            Utils.log(LOG_TAG, "安装新应用结果 -> " + result.status)

            mInstallingVaPathSet.remove(downloadEntity.path)

            mPackageInstalledLiveData.postValue(result.packageName)

            VBackupHelper.backupDBToExternalStorage(HaloApp.getInstance())
        }
    }

    /**
     * 游戏是否正在安装中
     */
    fun isInstalling(packageName: String): Boolean {
        val downloadEntity = getDownloadEntityByPackageName(packageName) ?: return false
        return (mInstallingVaPathSet.contains(downloadEntity.path))
    }

    private fun getDownloadEntityByPackageName(packageName: String): DownloadEntity? {
        return DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
            ?: getDownloadEntitySnapshot(gameId = null, packageName = packageName)
    }

    private fun insertInstalledGameToProvider(downloadEntity: DownloadEntity) {
        // 切换到主线程是为了避免 ConcurrentModificationException
        runOnUiThread {
            val values = ContentValues()
            val packageName = downloadEntity.packageName
            if (packageName.isEmpty()) return@runOnUiThread

            //主键
            values.put("package_name", packageName)
            values.put("url", downloadEntity.url)
            values.put("name", downloadEntity.name)
            values.put("size", downloadEntity.size)
            values.put("meta", GsonUtils.toJson(downloadEntity.meta))
            val type = if (PackageFlavorHelper.IS_TEST_FLAVOR) "test_flavor" else ""
            values.put("type", type)
            val uri = Uri.parse("content://com.lg.core.provider/download_game")

            runOnIoThread(true) {
                HaloApp.getInstance().contentResolver.insert(uri, values)
            }
        }
    }

    /**
     * 安装或启动应用
     */
    @JvmStatic
    fun installOrLaunch(context: Context, packageName: String) {
        Utils.log(LOG_TAG, "检测是需要安装还是启动 $packageName")

        validateVSpaceBeforeAction(context, null) {
            if (isInstalled(packageName)) {
                launch(context, packageName)
            } else {
                // 这里重新检查一遍是否已安装，因为有可能因为上一次更新列表失败而出现重新下载的情况
                if (isInstalled(packageName)) {
                    launch(context, packageName)
                    return@validateVSpaceBeforeAction
                }

                // 检查下载管理是否有下载实体，有实体表明未安装成功
                val downloadEntity = getDownloadEntityByPackageName(packageName)

                if (downloadEntity != null) {
                    val downloadedFile = File(downloadEntity.path)
                    if (downloadedFile.exists() && downloadedFile.length() == downloadEntity.size) {
                        install(context, downloadEntity, true)
                    } else {
                        // 重新下载
                        updateOrReDownload(downloadEntity, null)
                    }
                } else {
                    ToastUtils.toast("该游戏已损坏，请重新下载")
                }
            }
        }
    }

    /**
     * 启动应用
     */
    fun launch(context: Context, packageName: String) {
        Utils.log(LOG_TAG, "打开应用 $packageName")

        try {
            val intent = mDelegateManager.getStartGameIntent(packageName)
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            context.startActivity(intent)

            mLastSuccessfullyLaunchedGame = Pair(System.currentTimeMillis(), packageName)

            runOnIoThread {
                updateVGamePlayedTime(packageName, System.currentTimeMillis(), getPlayedTime(packageName))
                VBackupHelper.backupDBToExternalStorage(HaloApp.getInstance())
            }
        } catch (e: Exception) {
            ToastUtils.toast(e.localizedMessage ?: "")
        }
    }

    /**
     * 卸载应用
     */
    fun uninstall(packageName: String?) {
        Utils.log(LOG_TAG, "卸载游戏 $packageName")

        if (packageName.isNullOrBlank()) return

        mVGameDao.delete(packageName)

        if (mVGameDao.getAll().isEmpty()) {
            VBackupHelper.removeAllDatabase(HaloApp.getInstance())
        } else {
            VBackupHelper.backupDBToExternalStorage(HaloApp.getInstance())
        }

        val uninstallClosure: () -> Unit = {
            try {
                val result = VirtualAppManager.get().uninstallGame(packageName)
                if (result) {
                    updateInstalledList()
                }

                Utils.log(LOG_TAG, "卸载应用结果 -> $result")
            } catch (e: Exception) {
                ToastUtils.toast(e.localizedMessage ?: "")
            }
        }

        if (mDelegateManager.isConnectAidlInterface) {
            uninstallClosure.invoke()
        } else {
            connectService(false, uninstallClosure)
        }
    }

    /**
     * 获取游戏最后打开的时间
     * @return 最后打开的时间戳
     */
    fun getLastPlayedTime(downloadEntity: DownloadEntity): Long {
        val lastPlayedTimeString = downloadEntity.getMetaExtra(KEY_LAST_PLAYED_TIME)
        return if (TextUtils.isEmpty(lastPlayedTimeString)) 0 else lastPlayedTimeString.toLong()
    }

    /**
     * 获取游戏的游玩时长
     */
    fun getTotalPlayedTime(packageName: String): Long {
        return mInstalledInfoList.firstOrNull { it.packageName == packageName }?.appTotalPlayTime ?: 0L
    }

    /**
     * 根据游戏 ID 和包名获取下载快照
     */
    @JvmStatic
    fun getDownloadEntitySnapshot(gameId: String?, packageName: String?): DownloadEntity? {
        return getVGameSnapshot(gameId, packageName)?.downloadEntity
    }

    /**
     * 更新数据库重中的畅玩游戏
     */
    private fun updateVGamePlayedTime(packageName: String, lastPlayedTime: Long, totalPlayedTime: Long) {
        getVGameSnapshot(null, packageName)?.let {
            it.downloadEntity.addMetaExtra(KEY_LAST_PLAYED_TIME, lastPlayedTime.toString())
            it.downloadEntity.addMetaExtra(KEY_TOTAL_PLAYED_TIME, totalPlayedTime.toString())
            try {
                mVGameDao.insert(it)
            } catch (e: SQLiteFullException) {
                ToastUtils.toast("设备存储空间不足，请清理后重试")
            }
        }

        // 更新首页排序
        EventBus.getDefault().post(EBReuse("vgame"))
    }

    /**
     * 获取畅玩安装完成的 LiveData，内容是包名
     */
    fun getPackageInstalledLiveData(): LiveData<String> = mPackageInstalledLiveData

    fun showFloatingWindow(packageName: String) {
        val vDownloadList = DownloadManager.getInstance().allVDownloadTaskSnapshots
        vDownloadList.firstOrNull {
            it.packageName == packageName
        }?.let {
            val topActivity = AppManager.getInstance().currentActivity() ?: return
            if (topActivity.isFinishing || topActivity is VSpaceLoadingActivity) return

            VLoadCompleteWindowHelper.showFloatingWindow(topActivity, toGameEntity(it))
        }
    }

    /**
     * 畅玩空间是否已安装
     * 如果已安装或配置为空返回 true
     * 未安装的情况下会弹弹窗
     */
    fun showDialogIfVSpaceIsNeeded(context: Context): Boolean {
        Utils.log(LOG_TAG, "检测是否已安装畅玩空间")

        val vaConfig = Config.getVSettingEntity()?.va

        if (vaConfig == null) {
            ToastUtils.toast("畅玩空间暂未上线")
            return true
        }

        // TODO 检测 32 位
        if (!PackageUtils.isInstalled(context, vaConfig.arch64?.packageName)) {
            VSpaceDialogFragment.showDownloadDialog(context, getVSpaceDownloadEntity(false))
            Utils.log(LOG_TAG, "显示下载畅玩空间弹窗")
            return true
        }

        return false
    }

    private fun getVSpaceDownloadEntity(forUpdate: Boolean): AppEntity {
        return if (!forUpdate) {
            val appEntity = AppEntity()
            val vaConfig64 = Config.getVSettingEntity()?.va?.arch64
            appEntity.versionCode = vaConfig64?.versionCode ?: 0
            appEntity.version = vaConfig64?.versionName
            appEntity.url = vaConfig64?.url

            appEntity
        } else {
            mUpdateEntity!!
        }
    }

    /**
     * 更新或重下载
     */
    @JvmStatic
    fun updateOrReDownload(gameEntity: GameEntity) {
        PackagesManager.getUpdateList().firstOrNull { it.id == gameEntity.id }
            ?.let { updateEntity ->
                getVGameSnapshot(gameEntity.id, updateEntity.packageName)
                    ?.let { vGame ->
                        updateOrReDownload(vGame.downloadEntity, updateEntity)
                    }
            }
    }

    /**
     * 更新或重下载
     *
     * @param originDownloadEntity 旧的下载实体
     * @param updateEntity 更新内容，当 updateEntity 为空时重新下载
     */
    fun updateOrReDownload(originDownloadEntity: DownloadEntity, updateEntity: GameUpdateEntity? = null) {
        Utils.log(LOG_TAG, "更新应用${originDownloadEntity.packageName}")

        if (updateEntity != null) {
            originDownloadEntity.url = getVUrl(updateEntity.url)
            originDownloadEntity.gameId = updateEntity.id
            originDownloadEntity.name = updateEntity.name
            originDownloadEntity.eTag = updateEntity.etag
            originDownloadEntity.icon = updateEntity.icon
            originDownloadEntity.size = 0
            originDownloadEntity.platform = updateEntity.platform
            originDownloadEntity.packageName = updateEntity.packageName
            originDownloadEntity.versionName = updateEntity.version
            originDownloadEntity.isUpdate = true
            originDownloadEntity.addMetaExtra(Constants.RAW_GAME_ICON, updateEntity.rawIcon)
            originDownloadEntity.addMetaExtra(
                Constants.GAME_ICON_SUBSCRIPT,
                updateEntity.iconSubscript
            )
            originDownloadEntity.addMetaExtra(Constants.APK_MD5, updateEntity.md5)

            PackageRepository.removeUpdate(updateEntity.id, true)
        }

        originDownloadEntity.progress = 0
        originDownloadEntity.finalRedirectedUrl = ""
        originDownloadEntity.percent = 0.0

        // 确定下载类型
        val downloadType =
            if (updateEntity == null) ExposureUtils.DownloadType.FUN_DOWNLOAD else ExposureUtils.DownloadType.FUN_UPDATE
        val gameEntity = GameEntity(originDownloadEntity.gameId, originDownloadEntity.name)
        gameEntity.gameVersion = originDownloadEntity.versionName ?: ""

        val event = ExposureUtils.logADownloadExposureEvent(
            gameEntity,
            originDownloadEntity.platform,
            null,
            downloadType
        )

        originDownloadEntity.exposureTrace = GsonUtils.toJson(event)

        HistoryHelper.insertGameEntity(gameEntity)
        DownloadManager.getInstance().add(originDownloadEntity)

        // 收集下载数据
        DataCollectionUtils.uploadDownload(HaloApp.getInstance(), originDownloadEntity, "开始")
    }

    /***
     * 实体转换为普通游戏实体
     */
    fun toGameEntity(downloadEntity: DownloadEntity): GameEntity {
        val lastPlayedTimeString = downloadEntity.getMetaExtra(KEY_LAST_PLAYED_TIME)

        return GameEntity(id = downloadEntity.gameId, name = downloadEntity.name).apply {
            setApk(
                arrayListOf(
                    ApkEntity(
                        packageName = downloadEntity.packageName,
                        url = downloadEntity.url,
                        platform = downloadEntity.platform,
                        version = downloadEntity.versionName
                    )
                )
            )
            icon = downloadEntity.icon
            rawIcon = downloadEntity.getMetaExtra(Constants.RAW_GAME_ICON)
            iconSubscript = downloadEntity.getMetaExtra(Constants.GAME_ICON_SUBSCRIPT)
            lastPlayedTime = if (lastPlayedTimeString == "") 0L else lastPlayedTimeString.toLong()
            playedTime = getPlayedTime(downloadEntity.packageName, downloadEntity)
            downloadStatus = "smooth"
            setEntryMap(DownloadManager.getInstance().getEntryMap(name))
        }
    }

    /**
     * 获取畅玩游戏游玩时长
     */
    private fun getPlayedTime(packageName: String, downloadEntity: DownloadEntity? = null): Long {
        var playedTime = mInstalledInfoList.firstOrNull { it.packageName == packageName }?.appTotalPlayTime ?: 0L

        val cachedPlayedTimeString = downloadEntity?.getMetaExtra(KEY_TOTAL_PLAYED_TIME)
        if (playedTime == 0L && !cachedPlayedTimeString.isNullOrEmpty()) {
            playedTime = cachedPlayedTimeString.toLong()
        }

        return playedTime
    }

    /**
     * 获取畅玩空间更新
     */
    @SuppressLint("CheckResult")
    private fun getVSpaceUpdate(config: VSetting.VaArch) {
        val installedVersionName = PackageUtils.getVersionNameByPackageName(config.packageName)
        val installedVersionCode = PackageUtils.getVersionCodeByPackageName(config.packageName)

        RetrofitManager.getInstance()
            .vApi
            .getPackageUpdate(installedVersionName, installedVersionCode, config.packageName)
            .subscribeOn(Schedulers.io())
            .subscribe(object : BiResponse<AppEntity>() {
                override fun onSuccess(data: AppEntity) {
                    mUpdateEntity = data
                }
            })
    }

    /**
     * 通过 PackageRepository 检查更新
     */
    private fun checkUpdateViaPackageRepository() {
        val rawInstalledPackageList = getInstalledPackageList()
        val validInstalledPackageList = arrayListOf<String>()
        for (packageName in rawInstalledPackageList) {
            if (getVGameSnapshot(null, packageName) != null) {
                validInstalledPackageList.add(packageName)
            }
        }
        PackageRepository.addInstalledGames(validInstalledPackageList, true)
    }

    /**
     * 是否需要显示畅玩助手更新
     */
    private fun shouldShowVSpaceUpdate(
        updateEntity: AppEntity?,
        installedSpaceVersionCode: Int
    ): Boolean {
        if (updateEntity == null) return false

        val hasNewerVersion = installedSpaceVersionCode < updateEntity.versionCode

        if (!hasNewerVersion) return false

        if (updateEntity.isAlertEveryTime()) return true

        if (updateEntity.isAlertOnceADay()
            && SPUtils.getString(KEY_LAST_ALERT_UPDATE_URL) != (mUpdateEntity!!.url + mUpdateEntity!!.alert)
        ) {
            return true
        }

        return false
    }

    /**
     * 打开畅玩广场（版块）
     */
    fun startVSpaceSquare(context: Context) {
        val entity = SubjectRecommendEntity(link = "62fc8047b07e0c0bb63058c2", nameNormal = "畅玩广场", text = "畅玩广场")
        DirectUtils.directToBlock(context, entity, "")
    }

    /**
     * 恢复畅玩数据
     */
    @JvmStatic
    fun recoverVDataIfPossible() {
        VBackupHelper.recoverValidData(HaloApp.getInstance())
    }

    /**
     * 获取畅玩游戏的下载地址（为了区分测试时与普通游戏一样的下载地址）
     */
    fun getVUrl(originUrl: String?): String {
        if (originUrl?.endsWith("type=v") == false) {
            return if (originUrl.contains("?")) {
                "$originUrl&type=v"
            } else {
                "$originUrl?type=v"
            }
        }
        return originUrl ?: ""
    }

    /**
     * 获取未经畅玩处理的原始下载地址
     */
    fun getOriginalUrl(vUrl: String?): String {
        if (vUrl?.endsWith("type=v") == true) {
            return vUrl.removeSuffix("&type=v").removeSuffix("?type=v")
        }
        return vUrl ?: ""
    }

    /**
     * 畅玩功能是否用过
     */
    fun isVIsUsed(): Boolean = SPUtils.getBoolean(KEY_V_IS_USED)

    /**
     * 畅玩功能是否启用
     */
    @JvmStatic
    fun isVGameOn() = BuildConfig.DEBUG
            || (BuildConfig.IS_VGAME_ON && Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Config.isVGameEnabled())

    /**
     * 检查畅玩游戏是否满足生成存档要求
     */
    fun checkArchiveExist(packageName: String, archiveConfigStr: String): Boolean {
        return try {
            mDelegateManager.checkArchiveExist(packageName, archiveConfigStr)
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }
}