package com.gh.gamecenter.packagehelper

import android.annotation.SuppressLint
import android.text.TextUtils
import androidx.lifecycle.MutableLiveData
import com.gh.common.filter.RegionSettingHelper
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.common.util.GameUtils
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.R
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.ObservableUtil
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.secondOrNull
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.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.json.JSONException
import org.json.JSONObject
import java.util.*

/**
 * 该类存储的是已安装的所有游戏(助手后台已收录的)和所有更新(包括插件化)数据
 *
 * 注意：不要直接在该类对数据进行 新增/删除/修改/查询 操作
 * 本类不开放手动对数据进行 新增/删除/修改 操作的方法, 如有需要直接修改本类加载逻辑
 * 过滤操作可委托[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 mInstalledPkgList = Collections.synchronizedList(ArrayList<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 = ArrayList<GameUpdateEntity>()
    val currentVersionList = ArrayList<GameUpdateEntity>() // 当前版本游戏的列表

    /**
     * 预留方法，如果想手动初始化可以调用
     */
    @JvmStatic
    fun initData() {
        if (mIsInitialisingData) return

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

            val list = PackageUtils.getAllPackageName(mApplication)

            uploadAppList()

            initFilterPackage(list) { filteredList ->
                mIsInitialisingData = false

                mInstalledPkgList.addAll(filteredList)
                notifyInstallPkgData()

                loadInstalledGameDigestAndNotifyData(filteredList)
            }

            loadGhzsUpdate()
        }
    }

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

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

    /**
     * 把助手更新数据添加到下载管理(为了不改变原有的更新逻辑只能在这里多请求一次接口)
     */
    private fun loadGhzsUpdate() {
        mApi.getUpdate(PackageUtils.getGhVersionName(), PackageUtils.getGhVersionCode(), HaloApp.getInstance().channel)
            .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 =
                            HaloApp.getInstance().application.getString(R.string.ghzs_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("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 matchVGameOnly 是否仅匹配畅玩游戏
     */
    @SuppressLint("CheckResult")
    private fun loadInstalledGameDigestAndNotifyData(
        filteredList: ArrayList<String>,
        onWorkerThreadOnly: Boolean = false,
        matchVGameOnly: 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(PackageFilterManager.packageKey, page, PAGE_SIZE)
                .subscribeOn(Schedulers.io())

            if (!onWorkerThreadOnly) {
                // 这里面的代码(根据包名获取签名？)或许是造成安装完成后的 ANR 和 startForegroundService did not then call startForeground 的原因
                // 为了避免影响其它地方，这里只处理安装完成后的调用
                observable = observable.observeOn(AndroidSchedulers.mainThread())
            }

            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) {
                                // 仅匹配畅玩游戏时，非畅玩游戏直接跳过
                                if (matchVGameOnly && !game.isVGame()) {
                                    continue
                                }

                                if (gh_id == null || gh_id == game.id) {
                                    gameInstalled.add(
                                        GameInstall.transformGameInstall(game, pkgName)
                                    )
                                    mInstalledGameList.add(game)
                                    val isCanPluggable = checkGamePlugin(game, pkgName)
                                    val isCanUpdate = checkGameUpdate(game)
                                    addCurrentlyInstalledVersionIfValid(game)
                                    if (!isNotifyUpdate && isCanUpdate || isCanPluggable) {
                                        isNotifyUpdate = true
                                    }
                                }
                            }
                        }
                    }
                    latch.countDown()
                }

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

    /**
     * 检查游戏更新
     * @param game
     * @return 该请求是否存在可更新的游戏
     */
    private fun checkGameUpdate(game: GameEntity): Boolean {
        val updateList = PackageUtils.getUpdateData(game)
        if (updateList.size > 0) {
            for (updateEntity in updateList) {
                addUpdateOrPluggable(updateEntity)
            }
            return true
        } else if (game.isVGame()) {
            // 畅玩游戏移除更新，避免死循环更新
            removeUpdate(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()) {
                        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) {
                isExist = true
            }
        }
        if (!isExist) gameUpdate.add(data)
    }

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

    /**
     * 新增已安装的游戏
     * @param pkgName 已安装的游戏包名
     */
    fun addInstalledGame(pkgName: String) {
        mInstalledPkgList.add(pkgName)
        notifyInstallPkgData()

        val list = arrayListOf(pkgName)

        updateFilterPackage(list) {
            loadInstalledGameDigestAndNotifyData(list, true)
        }
    }

    /**
     * 批量新增已安装的游戏数量
     * @param pkgNameList 数组列表
     */
    fun addInstalledGames(pkgNameList: ArrayList<String>, isVGameOnly: Boolean = false) {
        // 畅玩游戏不添加至本地的已安装包名列表中
        if (!isVGameOnly) {
            mInstalledPkgList.addAll(pkgNameList)
        }
        notifyInstallPkgData()

        updateFilterPackage(pkgNameList) {
            loadInstalledGameDigestAndNotifyData(pkgNameList, true, isVGameOnly)
        }
    }

    /**
     * 新增卸载游戏
     * @param pkgName 已安装的游戏包名
     */
    fun addUninstalledGame(pkgName: String) {
        // TODO 检查为什么会有两个相同的包名添加到 mInstalledPkgList 里
        mInstalledPkgList.removeAll { it == pkgName }
        // 尝试从临时的当前版本列表里移除已卸载的条目
        tryCatchInRelease {
            val iterator = currentVersionList.iterator()
            while (iterator.hasNext()) {
                if (iterator.next().packageName == pkgName) {
                    iterator.remove()
                }
            }
        }
        notifyInstallPkgData()

        // 删除更新列表
        var j = 0
        while (j < gameUpdate.size) {
            val game = gameUpdate[j]
            if (game.packageName == pkgName) {
                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()
    }

    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() {
        PackagesManager.initInstallPkgList(mInstalledPkgList)
    }

}