package com.gh.gamecenter.packagehelper

import android.annotation.SuppressLint
import android.os.Build
import android.text.TextUtils
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.util.GameUtils
import com.gh.common.util.PackageHelper
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.InstalledPackagesAction
import com.gh.gamecenter.common.exposure.meta.MetaUtil
import com.gh.gamecenter.common.loghub.LoghubUtils
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.EmptyResponse
import com.gh.gamecenter.common.retrofit.ObservableUtil
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.secondOrNull
import com.gh.gamecenter.common.utils.toArrayList
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.AppEntity
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.entity.PackageGame
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.GameInstall
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository.gameInstalled
import com.gh.gamecenter.packagehelper.PackageRepository.gameUpdate
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.Single
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.greenrobot.eventbus.EventBus
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import kotlin.collections.HashSet

/**
 * 该类存储的是已安装的所有游戏(助手后台已收录的)和所有更新(包括插件化)数据
 *
 * 注意：不要直接在该类对数据进行 新增/删除/修改/查询 操作
 * 本类不开放手动对数据进行 新增/删除/修改 操作的方法, 如有需要直接修改本类加载逻辑
 * 过滤操作可委托[PackagesManager] 处理
 * 查询操作统一由 [PackagesManager] 处理
 *
 * 游戏更新以及可插件化的数据统一放在 [gameUpdate] 由 [GameUpdateEntity.isPluggable] 字段区分
 * 已安装的应用列表[gameInstalled] 存在一个包对应多个游戏问题,如果需要一对一过滤可参考[PackagesManager.filterSameApk]
 */
object PackageRepository {

    private val mApplication = HaloApp.getInstance().application
    private val mApi = RetrofitManager.getInstance().api
    private val mNewApi = RetrofitManager.getInstance().newApi

    private const val LAST_UPLOAD_APPLIST_TIME = "last_upload_applist_time"
    private const val PAGE_SIZE = 50

    private val mInstalledPkgSet = Collections.synchronizedSet(HashSet<String>())

    @Volatile
    private var mIsInitialisingData = false

    val gameUpdateLiveData = MutableLiveData<List<GameUpdateEntity>>()
    val gameInstalledLiveData = MutableLiveData<List<GameInstall>>()

    private val mInstalledGameList = Collections.synchronizedList(ArrayList<GameEntity>())

    val gameInstalled = Collections.synchronizedList(ArrayList<GameInstall>())
    val gameUpdate = Collections.synchronizedList(ArrayList<GameUpdateEntity>())
    val currentVersionList = Collections.synchronizedList(ArrayList<GameUpdateEntity>()) // 当前版本游戏的列表

    private val _recentVaPlayedChanged = MutableLiveData<Unit>()
    val recentVaPlayedChanged: LiveData<Unit> = _recentVaPlayedChanged
    fun changeRecentVaPlayed() {
        _recentVaPlayedChanged.postValue(Unit)
    }
    val packageFilterManager = PackageFilterManager()
    var mPendingPackageNameSet = Collections.synchronizedSet<String>(hashSetOf<String>()) // 因遇到异常而等待下次操作更新的包名列表
    var mValidPackageNameSet = hashSetOf<String>() // 已被收录的游戏包名列表


    /**
     * 预留方法，如果想手动初始化可以调用
     */
    fun initData(onPackageNameReceivedCallback: (() -> Unit)? = null) {
        if (mIsInitialisingData) return

        mIsInitialisingData = true
        runOnIoThread {
            if (gameInstalled.isNotEmpty()) gameInstalled.clear()
            if (mInstalledGameList.isNotEmpty()) mInstalledGameList.clear()
            if (gameUpdate.isNotEmpty()) gameUpdate.clear()
            if (mInstalledPkgSet.isNotEmpty()) mInstalledPkgSet.clear()

            Single.zip<Result<Any?>, Result<Any?>, Result<Any?>>(
                Single.create { emitter ->
                    val list = PackageHelper.getInstalledPackageNameList(mApplication, 0)
                    uploadAppList()

                    // 回调包名获取结果
                    onPackageNameReceivedCallback?.invoke()

                    initFilterPackage(list) { filteredList ->
                        emitter.onSuccess(Result.success(null))

                        mInstalledPkgSet.addAll(filteredList)
                        notifyInstallPkgData()
                        loadInstalledGameDigestAndNotifyData(
                            packageKey = packageFilterManager.packageKey,
                            filteredList = filteredList,
                            isVGame = false,
                            updateInstallStatus = true,
                            fromInit = true
                        )
                    }
                },
                Single.create { emitter ->
                    // 畅玩游戏更新
                    var allGames = VHelper.getAllVGameSnapshots()
                    if (allGames.isEmpty()) {
                        VHelper.refreshVGameSnapshot()
                        allGames = VHelper.getAllVGameSnapshots()
                    }
                    val allGamePkgNames = allGames.map { it.packageName }.toArrayList()
                    if (allGamePkgNames.isNotEmpty()) {
                        val packageFilterManager = PackageFilterManager()
                        updateFilterPackage(packageFilterManager, allGamePkgNames) {
                            emitter.onSuccess(Result.success(null))
                            loadInstalledGameDigestAndNotifyData(
                                packageKey = packageFilterManager.packageKey,
                                filteredList = allGamePkgNames,
                                isVGame = true
                            )
                        }
                    }else{
                        emitter.onSuccess(Result.success(null))
                    }
                }
            ) { t1, t2 -> Result.success(null) }.subscribe(object : SingleObserver<Result<Any?>> {
                override fun onSubscribe(d: Disposable) {
                }

                override fun onSuccess(t: Result<Any?>) {
                    mIsInitialisingData = false
                }

                override fun onError(e: Throwable) {
                }
            })

            loadGhzsUpdate()
        }
    }

    /**
     * 初始化光环后台收录的安装包
     */
    private fun initFilterPackage(
        list: List<String>,
        callbackClosure: ((list: ArrayList<String>) -> Unit)? = null
    ) {
        packageFilterManager.updateFilterPackages(list, false, callbackClosure)
    }

    private fun updateFilterPackage(
        packageFilterManager: PackageFilterManager,
        list: MutableList<String>,
        callbackClosure: ((list: ArrayList<String>) -> Unit)? = null
    ) {
        packageFilterManager.updateFilterPackages(list, true, callbackClosure)
    }

    /**
     * 把助手更新数据添加到下载管理(为了不改变原有的更新逻辑只能在这里多请求一次接口)
     */
    private fun loadGhzsUpdate() {
        mApi.getUpdate(
            PackageUtils.getGhVersionName(),
            PackageUtils.getGhVersionCode(),
            HaloApp.getInstance().channel,
            Build.VERSION.SDK_INT
        )
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Response<AppEntity>() {
                override fun onResponse(appEntity: AppEntity?) {
                    if (appEntity != null && appEntity.versionCode > PackageUtils.getGhVersionCode()) {
                        val gameUpdateEntity = GameUpdateEntity()
                        gameUpdateEntity.name = "光环助手V" + appEntity.version!!
                        gameUpdateEntity.packageName = HaloApp.getInstance().application.packageName
                        gameUpdateEntity.size = appEntity.size
                        gameUpdateEntity.version = appEntity.version
                        gameUpdateEntity.url = appEntity.url
                        gameUpdateEntity.platform = "官方版"
                        gameUpdateEntity.id = Constants.GHZS_GAME_ID
                        gameUpdate.add(gameUpdateEntity)
                        notifyGameUpdateData()
                    }
                }
            })
    }

    /**
     * 上传已安装的应用数据 (供外部使用)
     */
    @JvmStatic
    fun checkAndUploadAppList() {
        uploadAppList()
    }

    /**
     * 上传已安装的应用数据，data 后台和 api 后台数据独立
     */
    @SuppressLint("CheckResult")
    private fun uploadAppList() {
        // 检查是否符合应用上报周期
        val lastUploadTime = SPUtils.getLong(LAST_UPLOAD_APPLIST_TIME, 0)

        // 上传到 api 后台一天一个周期
        if (Utils.getTime(mApplication) - lastUploadTime >= (3600 * 24)) {

            val appList = PackageUtils.getAppList(mApplication)
            val dataObject = JSONObject()
            val wrapperObject = JSONObject()
            try {
                dataObject.put("type", "PUT")
                dataObject.put("device_id", HaloApp.getInstance().gid)
                dataObject.put("oaid", HaloApp.getInstance().oaid)
                dataObject.put("app", appList)
                dataObject.put("jnfj", MetaUtil.getBase64EncodedIMEI())
                dataObject.put("user_id", UserManager.getInstance().userId)
                dataObject.put("time", Utils.getTime(HaloApp.getInstance().application))
                wrapperObject.put("content", dataObject.toString())
            } catch (e: JSONException) {
                e.printStackTrace()
            }
            LoghubUtils.log(wrapperObject, "halo-api-device-installed", true)

            SPUtils.setLong(LAST_UPLOAD_APPLIST_TIME, System.currentTimeMillis() / 1000);
        }
    }

    /**
     * 获取已安装游戏的数据以及检查游戏更新[checkGameUpdate]/插件化[checkGamePlugin]
     * 如果有数据变动会调用[gameUpdateLiveData] [gameInstalledLiveData]实现相关页面刷新
     *
     * @param filteredList 已安装的游戏包名集合 (仅已收录部分)
     * @param onWorkerThreadOnly 是否在工作线程执行
     * @param isVGame 包名列表是否为畅玩游戏
     * @param updateInstallStatus 更新安装状态 （通过 EventBus 来进行）
     * @param fromInit 是否来自数据首次初始化
     */
    @SuppressLint("CheckResult")
    private fun loadInstalledGameDigestAndNotifyData(
        packageKey: String,
        filteredList: ArrayList<String>,
        isVGame: Boolean = false,
        updateInstallStatus: Boolean = false,
        fromInit: Boolean = false
    ) {
        var isNotifyUpdate = false
        val maxPageCount = (filteredList.size / PAGE_SIZE) + 1
        var page = 0

        val latch = ObservableUtil.latch(maxPageCount, {
            if (isNotifyUpdate || gameUpdateLiveData.value == null) notifyGameUpdateData()
            notifyGameInstallData()
        }, Any())

        while (++page <= maxPageCount) {
            var observable = mNewApi.getPackageGames(packageKey, page, PAGE_SIZE)
                .subscribeOn(Schedulers.io())

            observable.subscribe(object : BiResponse<List<PackageGame>>() {
                override fun onSuccess(data: List<PackageGame>) {
                    if (!data.isNullOrEmpty()) {
                        for (packageGame in data) {
                            val pkgName = packageGame.packageName
                            val gh_id = PackageUtils.getMetaData(HaloApp.getInstance().application, pkgName, "gh_id")
                            val validGames = RegionSettingHelper.filterGame(packageGame.games).apply {
                                for (game in this) {
                                    ApkActiveUtils.filterHideApk(game)
                                }
                            }

                            for (game in validGames) {
                                val shouldNotifyChanges =
                                    validateGameAndPostChanges(gh_id, game, pkgName, isVGame, updateInstallStatus, fromInit)
                                if (!isNotifyUpdate && shouldNotifyChanges) {
                                    isNotifyUpdate = true
                                }
                            }
                        }
                    }
                    latch.countDown()
                }

                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    latch.countDown()
                }
            })
        }
    }

    /**
     * 验证游戏并且更新数据
     */
    private fun validateGameAndPostChanges(
        ghId: Any?,
        game: GameEntity,
        pkgName: String,
        isVGame: Boolean,
        updateInstallStatus: Boolean,
        fromInit: Boolean = false
    ): Boolean {
        if (ghId == null || ghId == game.id) {
            gameInstalled.add(GameInstall.transformGameInstall(game, pkgName, isVGame))
            mInstalledGameList.add(game)
            val isCanPluggable = checkGamePlugin(game, pkgName)
            val isCanUpdate = checkGameUpdate(game, isVGame)
            addCurrentlyInstalledVersionIfValid(game)

            if (updateInstallStatus) {
                EventBus.getDefault()
                    .post(EBPackage(EBPackage.TYPE_INSTALLED, pkgName, game.getApk().firstOrNull()?.version, fromInit))
            }

            if (isCanUpdate || isCanPluggable) {
                return true
            }
        }

        return false
    }

    /**
     * 检查游戏更新
     * @param game
     * @return 该请求是否存在可更新的游戏
     */
    private fun checkGameUpdate(game: GameEntity, isVGame: Boolean): Boolean {
        val updateList = PackageUtils.getUpdateData(game, isVGame)
        if (updateList.size > 0) {
            for (updateEntity in updateList) {
                addUpdateOrPluggable(updateEntity)
            }
            return true
        } else if (isVGame) {
            // 畅玩游戏移除更新，避免死循环更新
            removeVGameUpdate(game.id, false)
            return true
        }

        return false
    }

    /**
     * 添加当前版本的信息如果满足条件的话
     */
    private fun addCurrentlyInstalledVersionIfValid(game: GameEntity) {
        for (apk in game.getApk()) {
            if (apk.version == PackageUtils.getVersionNameByPackageName(apk.packageName)
                && (TextUtils.isEmpty(apk.signature)
                        || apk.signature == PackageUtils.getApkSignatureByPackageName(mApplication, apk.packageName)
                    .firstOrNull()
                        || apk.signature == PackageUtils.getApkSignatureByPackageName(mApplication, apk.packageName)
                    .secondOrNull())
            ) {
                currentVersionList.add(GameUpdateEntity().apply {
                    id = game.id
                    name = game.name
                    icon = game.icon
                    packageName = apk.packageName
                    size = apk.size
                    version = apk.version
                    ghVersion = apk.ghVersion
                    url = apk.url
                    platform = apk.getPlatform()
                    etag = apk.etag
                    brief = game.brief
                    tagStyle = game.tagStyle
                    indexPlugin = game.indexPlugin
                    pluginDesc = game.pluginDesc
                    format = apk.format
                    signature = apk.signature ?: ""
                    currentVersion = PackageUtils.getVersionNameByPackageName(apk.packageName)
                    category = game.category
                })
            }
        }
    }

    /**
     * 检查游戏插件化
     * @param game
     * @param installedPkgName 已安装的游戏包名
     * @return 该请求是否存在可插件化的游戏
     */
    private fun checkGamePlugin(game: GameEntity, installedPkgName: String): Boolean {
        if (game.getApk().isNotEmpty()) {
            val apkList = game.getApk()
            for (apk in apkList) {
                if (apk.packageName == installedPkgName
                    && !TextUtils.isEmpty(apk.ghVersion)
                    && !PackageUtils.isSignedByGh(HaloApp.getInstance().application, apk.packageName)
                ) {
                    // 使用了镜像的游戏；插件化关闭的游戏；无需插件化
                    if (game.shouldUseMirrorInfo()
                        || apk.plugin == "close"
                    ) {
                        return false
                    }

                    // 如果该包是合集且安装了合集内任意一个光环的游戏包都不显示插件化
                    game.collection.forEach { collection ->
                        if (collection.packages.contains(apk.packageName)) {
                            collection.packages.forEach { collectionPackage ->
                                if (PackagesManager.isInstalled(collectionPackage)
                                    && PackageUtils.getGhId(collectionPackage).toString() == game.id
                                ) {
                                    return false
                                }
                            }
                        }
                    }

                    val updateEntity = GameUtils.getGameUpdateEntity(game, apk)
                    addUpdateOrPluggable(updateEntity)
                    return true
                }
            }
        }

        return false
    }

    /**
     * 添加更新或插件化数据(主要目的是排重)
     * @param data
     */
    private fun addUpdateOrPluggable(data: GameUpdateEntity) {
        var isExist = false
        for (entity in gameUpdate) {
            if (entity.packageName == data.packageName
                && entity.id == data.id
                && entity.isVGameUpdate == data.isVGameUpdate
            ) {
                isExist = true
            }
        }
        if (!isExist) gameUpdate.add(data)
    }

    /**
     * 移除畅玩更新
     * @param gameId 游戏 ID
     */
    fun removeVGameUpdate(gameId: String, notifyUpdate: Boolean) {
        gameUpdate.removeAll { it.id == gameId && it.isVGameUpdate }
        if (notifyUpdate) {
            notifyGameUpdateData()
        }
    }

    /**
     * 新增已安装的游戏
     * @param pkgName 已安装的游戏包名
     * @param cachedGameEntity 缓存的游戏实体，若存在免去再请求接口的过程
     */
    fun addInstalledGame(
        packageFilterManager: PackageFilterManager,
        pkgName: String,
        cachedGameEntity: GameEntity? = null
    ) {
        mInstalledPkgSet.add(pkgName)
        notifyInstallPkgData()

        if (cachedGameEntity != null) {
            val ghId = PackageUtils.getMetaData(HaloApp.getInstance().application, pkgName, "gh_id")
            val containsUpdate = validateGameAndPostChanges(
                ghId = ghId,
                game = cachedGameEntity,
                pkgName = pkgName,
                isVGame = false,
                updateInstallStatus = false
            )

            if (containsUpdate) {
                notifyGameUpdateData()
            }
        } else {
            val list = arrayListOf(pkgName)

            updateFilterPackage(packageFilterManager, list) {
                loadInstalledGameDigestAndNotifyData(
                    packageKey = packageFilterManager.packageKey,
                    filteredList = list,
                    updateInstallStatus = false
                )
            }
        }

        changeRecentVaPlayed()
    }

    /**
     * 批量新增已安装的游戏数量
     * @param pkgNameList 数组列表
     * @param isVGame 是否为畅玩游戏
     * @param updateInstallStatus 是否更新安装状态
     */
    fun addInstalledGames(
        packageFilterManager: PackageFilterManager,
        pkgNameList: ArrayList<String>,
        isVGame: Boolean = false,
        updateInstallStatus: Boolean = false,
    ) {
        // 畅玩游戏不添加至本地的已安装包名列表中
        if (!isVGame) {
            mInstalledPkgSet.addAll(pkgNameList)
        } else {
            changeRecentVaPlayed()
        }
        notifyInstallPkgData()

        updateFilterPackage(packageFilterManager, pkgNameList) {
            loadInstalledGameDigestAndNotifyData(
                packageKey = packageFilterManager.packageKey,
                filteredList = pkgNameList,
                isVGame = isVGame,
                updateInstallStatus = updateInstallStatus
            )
        }
    }

    /**
     * 新增卸载游戏
     * @param pkgName 已安装的游戏包名
     * @param isVGame 是否来自于畅玩游戏
     */
    fun addUninstalledGame(pkgName: String, isVGame: Boolean) {
        if (!isVGame && mInstalledPkgSet.isNotEmpty()) {
            mInstalledPkgSet.remove(pkgName)
        }
        // 尝试从临时的当前版本列表里移除已卸载的条目
        tryCatchInRelease {
            val iterator = currentVersionList.iterator()
            while (iterator.hasNext()) {
                if (iterator.next().packageName == pkgName) {
                    iterator.remove()
                }
            }
        }
        notifyInstallPkgData()

        // 删除更新列表
        synchronized(gameUpdate) {
            var j = 0
            while (j < gameUpdate.size) {
                val game = gameUpdate[j]
                // 仅类型一致时移除更新实体
                if (game.packageName == pkgName
                    && game.isVGameUpdate == isVGame
                ) {
                    gameUpdate.remove(game)
                    notifyGameUpdateData()
                } else {
                    j++
                }
            }
        }

        // 删除该已安装列表数据
        var i = 0
        while (i < gameInstalled.size) {
            val game = gameInstalled[i]
            if (game.packageName == pkgName) {
                gameInstalled.remove(game)
                if (mInstalledGameList.size > i) {
                    mInstalledGameList.remove(mInstalledGameList[i])
                }
            } else {
                i++
            }
        }
        notifyGameInstallData()
        changeRecentVaPlayed()
    }

    /**
     * 是否存在因为接口异常而导致没有查询收录情况的包名列表
     */
    fun hasPendingPackage(): Boolean {
        return mPendingPackageNameSet.isNotEmpty()
    }

    private fun notifyGameInstallData() {
        PackagesManager.initGameInstall(ArrayList(gameInstalled))
        gameInstalledLiveData.postValue(ArrayList(gameInstalled))
    }

    private fun notifyGameUpdateData() {
        PackagesManager.initGameUpdate(ArrayList(gameUpdate))
        gameUpdateLiveData.postValue(ArrayList(gameUpdate))
    }

    private fun notifyInstallPkgData() {
        val pkgSet = hashSetOf<String>()

        val iterator = mInstalledPkgSet.iterator()
        while (iterator.hasNext()) {
            pkgSet.add(iterator.next())
        }

        postInstalledPackageList(pkgSet)

        PackagesManager.initInstallPkgSet(pkgSet)
    }

    private fun postInstalledPackageList(pkgSet: HashSet<String>) {
        val requestBody = InstalledPackagesAction(
            action = "launch",
            packages = pkgSet.toList()
        ).toRequestBody()

        RetrofitManager.getInstance().newApi.postInstalledPackages(HaloApp.getInstance().gid, requestBody)
            .subscribeOn(Schedulers.io())
            .subscribe(EmptyResponse<ResponseBody>())
    }

}