package com.gh.gamecenter.gamedetail

import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.net.Uri
import android.os.Build
import android.text.TextUtils
import android.util.SparseBooleanArray
import androidx.collection.arrayMapOf
import androidx.lifecycle.*
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.history.HistoryHelper
import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.LibaoUtils
import com.gh.common.util.PackageHelper
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.ErrorEntity
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.entity.PKEntity
import com.gh.gamecenter.common.exposure.meta.MetaUtil
import com.gh.gamecenter.common.mvvm.Resource
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.entity.RecommendPopupEntity
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.entity.UnifiedUserTrendEntity
import com.gh.gamecenter.feature.entity.*
import com.gh.gamecenter.feature.exposure.ExposureConstants
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.feature.utils.ConcernUtils
import com.gh.gamecenter.feature.utils.ContentBlockedHelper
import com.gh.gamecenter.gamedetail.detail.viewholder.GameDetailContentRecommendItemViewHolder
import com.gh.gamecenter.gamedetail.entity.*
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.VHelper
import com.google.gson.JsonArray
import com.google.gson.reflect.TypeToken
import com.halo.assistant.HaloApp
import com.halo.assistant.accelerator.repository.AccelerationRepository
import com.lightgame.utils.Utils
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONObject
import retrofit2.HttpException
import tv.danmaku.ijk.media.exo2.ExoSourceManager

class GameDetailViewModel(
    application: Application,
    var gameId: String?,
    var game: GameEntity?,
    val entrance: String?
) : AndroidViewModel(application) {

    private val api = RetrofitManager.getInstance().api
    private val newApi = RetrofitManager.getInstance().newApi
    private val accelerationRepository = AccelerationRepository.newInstance()

    val concernLiveData = MutableLiveData<ConcernResponse>()
    val gameLiveData = MutableLiveData<Resource<GameEntity>?>()
    val bigEventLiveData = MutableLiveData<List<BigEvent>>()
    val recommendPopupLiveData = MutableLiveData<ArrayList<RecommendPopupEntity>>()
    val archiveStatusLiveData = MutableLiveData<Boolean>()

    val gameDetailTabListLiveData = MutableLiveData<Resource<List<GameDetailTabEntity>>>()
    val gameDetailSettingLiveData = MutableLiveData<GameDetailSetting>()
    val coverListLiveData = MutableLiveData<List<CoverEntity>>()
    val basicInfoLiveData = MutableLiveData<GameEntity?>()
    val dataInfoLiveData = MutableLiveData<List<GameDetailDataInfo>?>()
    val infoTagLiveData = MutableLiveData<GameDetailInfoTag?>()
    val detailDataListLiveData = MutableLiveData<List<GameDetailData>>()
    val zoneLiveData = MutableLiveData<ZoneEntity>()
    val meLiveData = MutableLiveData<MeEntity>()

    val pkVoteResultLiveData = MutableLiveData<Event<Pair<String, Boolean>>>()
    val scrollToListPositionLiveData = MutableLiveData<Event<LinkEntity>>()
    val tabSelectedLiveData = MutableLiveData<Event<LinkEntity>>()
    val contentCardClickedLiveData = MutableLiveData<Event<String>>()

    var videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true)
    var displayCoverVideo: Boolean = false

    // PK投票相关
    private val pkIdList = arrayListOf<String>()
    private val pkDataList = arrayListOf<PKEntity>()

    private var libaoList: ArrayList<LibaoEntity>? = null // 礼包
    private var libaoStatusList: List<LibaoStatusEntity>? = null // 礼包状态
    private var unifiedUserTrendEntity: UnifiedUserTrendEntity? = null // 用户相关

    // 大家都在玩相关
    private var recommendGame: GameDetailRecommendGame? = null
    private val recommendGameIdMaps = hashMapOf<String, String>()
    private var relatedGameList = arrayListOf<GameEntity>()

    // 内容卡片相关
    private var contentCardList: List<ContentCardEntity>? = null
    private val contentCardSp by lazy {
        HaloApp.getInstance().getSharedPreferences(SP_CONTENT_CARD, Context.MODE_PRIVATE)
    }

    val expandTextStatusSparseBooleanArray = SparseBooleanArray()
    val comprehensivePanelExpandTextStatusMap = hashMapOf<String, Boolean>()
    val comprehensivePanelExpandBtnStatusMap = hashMapOf<String, Boolean>()

    private var subjectPageMap = arrayMapOf<String, Int>()

    private val mirrorParams by lazy {
        if (game?.shouldUseMirrorInfo() == true) {
            mapOf("mirror_type" to game?.getMirrorPosition())
        } else mapOf()
    }

    var pageDepth = 0 // 浏览深度
    var isDownloadClick = false // 是否触发下载
    var isSkipOnPageSelected = false // 是否触发论坛/专区Tab跳转

    var defaultCoverEntity: CoverEntity? = null
    var coverTabSequence = 1 // 用于埋点，详情视频/图集tab 当前选中tab的序号，从1开始
    var coverTabName = "" // 用于埋点，详情视频/图集tab 详情视频/图集tab 当前选中tab的tab名称

    var isGameInstalled = false
    private var isGameUpdatable = false

    private val compositeDisposable = CompositeDisposable()

    private var userRelatedInfoReceivedCallback: (() -> Unit)? = null

    init {
        loadData()
    }

    fun loadData() {
        if (RegionSettingHelper.shouldThisGameBeFiltered(gameId)) {
            gameId = "invalid"
            game?.id = "invalid"
        }

        when {
            gameId != null -> getGameDigest()
            else -> gameLiveData.postValue(null)
        }
    }

    private fun loadGameDetailData() {
        updateGameStatus()
        getGameDetailData()
        getGameDetailSetting()
        getGameDetailZone()
        getRecommendPopup(game?.id ?: "")
    }

    fun updateGameStatus() {
        isGameInstalled = false
        game?.let {
            for (apkEntity in it.getApk()) {
                if (PackagesManager.isInstalled(apkEntity.packageName) || VHelper.isInstalled(apkEntity.packageName)) {
                    isGameInstalled = true
                    for (entity in PackagesManager.getUpdateList()) {
                        if (!entity.isPluggable
                            && entity.packageName == apkEntity.packageName
                            && entity.id == gameId
                        ) {
                            isGameUpdatable = true
                            break
                        }
                    }
                    break
                }
            }
        }
    }

    // 获取游戏摘要
    private fun getGameDigest() {
        api.getGameDigest(gameId)
            .map(ApkActiveUtils.filterMapper)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Response<GameEntity>() {
                override fun onResponse(response: GameEntity?) {
                    game = response
                    game?.subPageCode = ExposureConstants.GAME_DETAIL
                    gameLiveData.postValue(Resource.success(game))
                    loadGameDetailData()
                }

                override fun onFailure(e: HttpException?) {
                    gameLiveData.postValue(Resource.error(e))
                }
            })
    }


    /**
     * 过滤被屏蔽的内容
     */
    private fun filterBlockedContent(dataList: List<GameDetailData>): List<GameDetailData> {
        val finalItemList: ArrayList<GameDetailData> = arrayListOf()
        for (item in dataList) {
            if (ContentBlockedHelper.isContentBlocked(item.type)) {
                continue
            }
            if (item.type == GameDetailData.TYPE_BASIC_INFO && ContentBlockedHelper.isContentBlocked(
                    ContentBlockedHelper.GAME_TAG
                )
            ) {
                item.linkBasicInfo?.gameTags = arrayListOf()
            }
            finalItemList.add(item)
        }
        return finalItemList
    }

    //过滤游戏标签
    private fun filterGameTags(tagList: ArrayList<TagStyleEntity>) {
        val filterTagString = SPUtils.getString(Constants.SP_FILTER_TAGS)
        val filterTags = GsonUtils.fromJsonList<String>(filterTagString)
        var i = 0
        while (i < tagList.size) {
            if (filterTags.contains(tagList[i].name)) {
                tagList.removeAt(i)
                i--
            }
            i++
        }
    }

    //过滤其他相关游戏中屏蔽的游戏
    private fun filterRelatedVersionGames(data: ArrayList<GameEntity>) {
        var i = 0
        while (i < data.size) {
            if (RegionSettingHelper.shouldThisGameBeFiltered(data[i].id)) {
                data.removeAt(i)
                i--
            }
            i++
        }
    }

    fun refreshWrongInstallStatus() {
        game?.let {
            PackageHelper.refreshWrongInstallStatus(arrayListOf(it))
        }
    }

    fun concernCommand(isConcern: Boolean) {
        val meEntity = meLiveData.value ?: MeEntity()
        val listener = object : ConcernUtils.OnConcernListener {
            override fun onSuccess() {
                meLiveData.postValue(meEntity.apply { isGameConcerned = true })
                concernLiveData.postValue(ConcernResponse(isConcern, true))
            }

            override fun onError() {
                meLiveData.postValue(meEntity.apply { isGameConcerned = false })
                concernLiveData.postValue(ConcernResponse(isConcern, false))
            }
        }
        game?.id?.let {
            if (isConcern) {
                ConcernUtils.postConcernGameId(it, listener)
            } else {
                ConcernUtils.deleteConcernData(it, listener)
            }
        }
    }

    fun logHistory(data: GameEntity) {
        HistoryHelper.insertGameEntity(data)
        HistoryHelper.insertGameDetail(data)
    }

    fun removeFromHistory() {
        HistoryHelper.deleteGameEntity(gameId ?: "")
        HistoryHelper.deleteGameDetailEntity(gameId ?: "")
    }

    fun isTopVideoPartlyCached(topVideoUrl: String): Boolean {
        val cache = ExoSourceManager.getCacheSingleInstance(HaloApp.getInstance().application, null)
        val key = Uri.parse(topVideoUrl).toString()
        return if (!TextUtils.isEmpty(key)) {
            val cachedSpans = cache.getCachedSpans(key)
            cachedSpans.size != 0
        } else {
            false
        }
    }

    //获取大事件
    fun getBigEvent() {
        api.getBigEvent(game?.id)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Response<List<BigEvent>>() {
                override fun onResponse(response: List<BigEvent>?) {
                    super.onResponse(response)
                    response?.let {
                        for (event in it) {
                            if (event.link?.type == "feedback") {
                                event.link?.name = game?.name
                                event.link?.text = game?.getGameCategory().toString()
                            }
                        }
                        bigEventLiveData.postValue(it)
                    }
                }
            })
    }

    @SuppressLint("CheckResult")
    private fun getRecommendPopup(gameId: String) {
        api.getRecommendPopup(gameId)
            .compose(singleToMain())
            .subscribe(object : BiResponse<ArrayList<RecommendPopupEntity>>() {
                override fun onSuccess(data: ArrayList<RecommendPopupEntity>) {
                    recommendPopupLiveData.postValue(data)
                }
            })
    }

    @SuppressLint("CheckResult")
    private fun getArchiveStatus(gameId: String) {
        newApi.getArchiveStatus(gameId)
            .compose(singleToMain())
            .subscribe(object : BiResponse<ResponseBody>() {
                override fun onSuccess(data: ResponseBody) {
                    val body = JSONObject(data.string())
                    val hasNew = body.optBoolean("has_new")
                    archiveStatusLiveData.postValue(hasNew)
                }
            })
    }

    @SuppressLint("CheckResult")
    private fun getGameDetailSetting() {
        newApi.getGameDetailSetting(gameId, mirrorParams)
            .compose(singleToMain())
            .subscribe({
                gameDetailSettingLiveData.postValue(it)
            }, {})
    }

    @SuppressLint("CheckResult")
    private fun provideTabSingle(): Single<Resource<List<GameDetailTabEntity>>> =
        newApi.getGameDetailTab(gameId, mirrorParams)
            .map {
                if (it.isEmpty()) {
                    Resource.error(null)
                } else {
                    Resource.success(it)
                }
            }

    @SuppressLint("CheckResult")
    fun getGameDetailData() {
        provideTabSingle().compose(singleToMain())
            .subscribe({
                gameDetailTabListLiveData.postValue(it)
                it.data?.let { tabList ->
                    var finalDataList: List<GameDetailData>? = null
                    for (entity in tabList) {
                        if (entity.type == GameDetailTabEntity.TYPE_DETAIL && entity.defaultData != null) {
                            val sortedDataList =
                                entity.defaultData!!.sortedBy { data ->
                                    if (isGameUpdatable) data.order.updatable else if (isGameInstalled) data.order.installed else data.order.beforeDownload
                                }
                            finalDataList = filterBlockedContent(sortedDataList)
                            continue
                        }
                        if (entity.type == GameDetailTabEntity.TYPE_ARCHIVE) {
                            getArchiveStatus(game?.id ?: "")
                            continue
                        }
                    }

                    val detailDataList = if (finalDataList != null) {
                        processGameDetailListData(finalDataList)
                    } else {
                        arrayListOf()
                    }.onEachIndexed { index, itemData ->
                        itemData.position = index
                    }
                    detailDataListLiveData.postValue(detailDataList)

                    getGameDetailExtraData()
                }
            }, {
                gameDetailTabListLiveData.postValue(Resource.error(it as? HttpException))
            })
    }

    @SuppressLint("CheckResult")
    private fun getGameDetailExtraData() {
        val extraDataSingleList = arrayListOf<Single<Result<Any?>>>()
        if (pkIdList.isNotEmpty()) {
            // 获取PK数据
            extraDataSingleList.add(providePKDataSingle())
        }
        if (!libaoList.isNullOrEmpty()) {
            // 获取礼包状态
            extraDataSingleList.add(provideGiftStatusSingle())
        }
        if (game?.shouldUseMirrorInfo() == false && recommendGame != null) {
            // 非镜像游戏获取大家都在玩 (数据来源看具体方法内容) 数据
            extraDataSingleList.add(provideEveryonePlayingSingle())
        }
        if (CheckLoginUtils.isLogin() && zoneLiveData.value != null) {
            // 获取游戏详情内容相应的用户相关信息
            extraDataSingleList.add(provideUserRelatedInfoSingle())
        }
        if (extraDataSingleList.isNotEmpty()) {
            Single.zip(extraDataSingleList) {}
                .subscribeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    val newDetailDataList = ArrayList(detailDataListLiveData.value ?: return@subscribe)
                    processGameDetailListExtraData(newDetailDataList)
                    detailDataListLiveData.postValue(newDetailDataList.onEachIndexed { index, itemData ->
                        itemData.position = index
                    })
                }, {})
        }
    }

    @SuppressLint("CheckResult")
    private fun getGameDetailZone() {
        newApi.getGameDetailZone(gameId)
            .compose(singleToMain())
            .subscribe({
                zoneLiveData.value = it
                if (CheckLoginUtils.isLogin() && detailDataListLiveData.value != null) {
                    getUserRelatedInfo()
                }
            }, {
                Utils.toast(getApplication(), "获取专区数据失败")
            })
    }

    private fun processGameDetailListData(dataList: List<GameDetailData>): ArrayList<GameDetailData> {
        val detailDataList = arrayListOf<GameDetailData>()
        pkIdList.clear()
        for ((index, data) in dataList.withIndex()) {
            when {
                data.linkVideoImgArea != null -> {
                    val coverList = transformVideoImgAreaData(data.linkVideoImgArea)
                    if (coverList.isNotEmpty()) {
                        defaultCoverEntity =
                            coverList.find { coverEntity -> coverEntity.isDefault } ?: coverList.first()
                        coverListLiveData.postValue(coverList)
                    }
                }

                data.linkBasicInfo != null -> {
                    if (data.linkBasicInfo.gameTags.isNotEmpty()) {
                        filterGameTags(data.linkBasicInfo.gameTags)
                    }
                    if (!data.linkBasicInfo.event.isNullOrEmpty()) {
                        data.linkBasicInfo.event = data.linkBasicInfo.event!!.sortedByDescending { it.time }
                    }
                    basicInfoLiveData.postValue(data.linkBasicInfo)
                }

                data.linkDataInfo != null -> {
                    dataInfoLiveData.postValue(data.linkDataInfo)
                }

                data.linkInfoTag != null -> {
                    infoTagLiveData.postValue(data.linkInfoTag)
                }

                else -> {
                    data.linkContentCard?.let {
                        contentCardList = it.onEach { contentCardEntity ->
                            if (contentCardEntity.link.type == ContentCardEntity.TYPE_ARCHIVE && contentCardEntity.archive != null) {
                                contentCardEntity.showNewTag =
                                    SPUtils.getString(contentCardSp, gameId ?: "") != contentCardEntity.archive.id
                                SPUtils.setString(contentCardSp, gameId ?: "", contentCardEntity.archive.id)
                            }
                        }
                    }
                    if (data.linkContentRecommend?.type == GameDetailContentRecommendItemViewHolder.TYPE_PK && !data.linkContentRecommend.link.isNullOrEmpty()) {
                        data.linkContentRecommend.link?.let { pkIdList.add(it) }
                    }
                    if (data.linkEveryonePlaying != null) {
                        if (game?.shouldUseMirrorInfo() == true) continue
                        recommendGame = data.linkEveryonePlaying
                    }
                    if (data.linkServer != null && data.linkServer.calendar.isEmpty()) continue
                    if (data.linkColumnRecommend != null) {
                        data.linkColumnRecommend.forEach {
                            detailDataList.add(
                                GameDetailData(
                                    type = data.type,
                                    recommendColumn = it
                                )
                            )
                        }
                        continue
                    }
                    if (data.linkLibao != null) {
                        val simpleGame = game?.toSimpleGame()
                        libaoList = data.linkLibao?.onEach {
                            if (it.game == null) {
                                it.game = simpleGame
                            }
                        }
                    }
                    if (data.linkRelatedGame != null) {
                        filterRelatedVersionGames(data.linkRelatedGame)
                        if (data.linkRelatedGame.isEmpty()) {
                            continue
                        }
                    }
                    if (data.linkUpdate != null && data.linkUpdate.updateDes.isEmpty() && data.linkUpdate.version.isEmpty()) {
                        continue
                    }
                    if (data.linkCustomColumn != null) {
                        val lastCustomColumnData = dataList.getOrNull(index - 1)?.linkCustomColumn
                        val nextCustomColumnData = dataList.getOrNull(index + 1)?.linkCustomColumn

                        if (lastCustomColumnData != null) {
                            data.linkCustomColumn.subPosition = lastCustomColumnData.subPosition + 1
                            if (nextCustomColumnData == null) data.linkCustomColumn.showBottomPadding = true
                        } else if (nextCustomColumnData != null) {
                            data.linkCustomColumn.subPosition = 0
                        } else {
                            data.linkCustomColumn.subPosition = -1
                            data.linkCustomColumn.showBottomPadding = true
                        }
                    }

                    detailDataList.add(data)
                }
            }
        }

        return detailDataList
    }

    private fun processGameDetailListExtraData(detailDataList: ArrayList<GameDetailData>) {
        pkDataList.forEach { pkEntity ->
            val itemData = detailDataList.find { it.linkContentRecommend?.link == pkEntity.id }
            if (itemData != null) {
                itemData.pkData = pkEntity
            }
        }

        detailDataList.find { it.linkEveryonePlaying != null }?.let {
            if (relatedGameList.isNotEmpty()) {
                relatedGameList.shuffle()
                relatedGameList.forEach { game ->
                    game.subPageCode = ExposureConstants.GAME_DETAIL
                }
                val recommendedGames = SubjectEntity().apply { data = relatedGameList }
                it.linkEveryonePlaying?.recommendedGames = recommendedGames
            } else {
                detailDataList.remove(it)
            }
        }

        unifiedUserTrendEntity?.let { mergeUnifiedGameDetailWithUserRelatedInfo(it) }
        processLibaoData(detailDataList)
    }

    private fun processLibaoData(detailDataList: List<GameDetailData>) {
        libaoList?.let {
            libaoStatusList?.let { libaoStatusList -> LibaoUtils.initLiBaoEntity(libaoStatusList, libaoList) }
            detailDataList.find { itemData -> itemData.linkLibao != null }?.run {
                linkLibao = it
                sortLibaoList(linkLibao)
            }
        }
    }

    private fun transformVideoImgAreaData(dataList: List<CoverTabEntity>): List<CoverEntity> {
        val coverEntityList = arrayListOf<CoverEntity>()
        var index = 0
        dataList.forEach {
            if (it.video != null) {
                displayCoverVideo = true
                coverEntityList.add(
                    CoverEntity(
                        index = index++,
                        isDefault = it.select,
                        tabName = it.name,
                        video = it.video,
                        coverTabEntity = it
                    )
                )
            }
            if (it.gallery != null) {
                it.gallery!!.forEachIndexed { galleryIndex, gallery ->
                    coverEntityList.add(
                        CoverEntity(
                            index = index++,
                            galleryIndex = galleryIndex,
                            galleryCount = it.gallery?.size ?: 0,
                            isDefault = it.select && galleryIndex == 0,
                            tabName = it.name,
                            gallery = gallery,
                            coverTabEntity = it
                        )
                    )
                }
            }
        }
        return coverEntityList
    }

    @SuppressLint("CheckResult")
    private fun providePKDataSingle(): Single<Result<Any?>> = Single.create { emitter ->
        val data = mapOf(
            "pk_ids" to pkIdList
        )
        newApi.getPKList(data.toRequestBody())
            .compose(singleToMain())
            .subscribe({ pkList ->
                pkDataList.clear()
                pkDataList.addAll(pkList)
                emitter.onSuccess(Result.success(pkList))
            }, {
                Utils.toast(getApplication(), "获取PK数据失败")
                emitter.onSuccess(Result.failure(it))
            })
    }

    @SuppressLint("CheckResult")
    private fun provideGiftStatusSingle(): Single<Result<Any?>> = Single.create { emitter ->
        if (libaoList.isNullOrEmpty()) {
            emitter.onSuccess(Result.failure(Exception()))
            return@create
        }

        val builder = StringBuilder()
        var i = 0
        val size = libaoList!!.size
        while (i < size) {
            builder.append(libaoList!![i].id)
            builder.append("-")
            i++
        }
        builder.deleteCharAt(builder.length - 1)
        val ids = builder.toString()

        api.getLibaoStatus(UrlFilterUtils.getFilterQuery("libao_ids", ids))
            .compose(observableToMain())
            .subscribe({
                libaoStatusList = it
                emitter.onSuccess(Result.success(it))
            }, {
                Utils.toast(getApplication(), "获取礼包状态失败")
                emitter.onSuccess(Result.failure(it))
            })
    }

    @SuppressLint("CheckResult")
    private fun provideEveryonePlayingSingle(): Single<Result<Any?>> = Single.create { emitter ->
        recommendGameIdMaps.clear()
        val relatedGames = recommendGame?.relatedGames
        val installGames = recommendGame?.installGames
        val downloadGames = recommendGame?.downloadGames

        if (relatedGames.isNullOrEmpty()
            && installGames.isNullOrEmpty()
            && downloadGames.isNullOrEmpty()
        ) {
            emitter.onSuccess(Result.success(null))
        }

        val labelGames = arrayListOf<String>()
        for (relatedGame in relatedGames ?: listOf()) {
            relatedGame.game?.let {
                labelGames.addAll(it)
            }
        }

        if (labelGames.isNotEmpty()) {
            val gameIds = labelGames.take(4)
            gameIds.forEach {
                recommendGameIdMaps[it] = "标签推荐"
            }
        }
        val labelGameCount = recommendGameIdMaps.size

        if (installGames != null) {
            val gameIds = installGames.take(4)
            gameIds.forEach {
                recommendGameIdMaps[it] = "安装推荐"
            }
        }

        if (downloadGames != null) {
            val gameIds = downloadGames.take(4)
            gameIds.forEach {
                recommendGameIdMaps[it] = "下载推荐"
            }
        }

        if (recommendGameIdMaps.size < 6 && labelGames.size > labelGameCount) {
            for (i in labelGameCount until labelGames.size) {
                recommendGameIdMaps[labelGames[i]] = "标签推荐"
                if (recommendGameIdMaps.size >= 6) break
            }
        }

        if (recommendGameIdMaps.size < 6) {
            emitter.onSuccess(Result.success(null))
        }

        val ids = recommendGameIdMaps.map { it.key }.toList().joinToString("-")
        val filterQuery = UrlFilterUtils.getFilterQuery("game_ids", ids)
        api.getGamesDigestByIds(filterQuery)
            .compose(observableToMain())
            .subscribe({
                it.forEach { game ->
                    game.recommendType = recommendGameIdMaps[game.id] ?: ""
                }
                relatedGameList.clear()
                relatedGameList.addAll(it)
                emitter.onSuccess(Result.success(it))
            }, {
                Utils.toast(getApplication(), "获取大家都在玩数据失败")
                emitter.onSuccess(Result.failure(it))
            })
    }

    @SuppressLint("CheckResult")
    private fun provideUserRelatedInfoSingle(): Single<Result<Any?>> = Single.create { emitter ->
        val libaoIdList = libaoList?.map { it.id ?: "" } ?: emptyList()
        val toolkitIdList = arrayListOf<String>()
        val answerIdList = arrayListOf<String>()

        val zone = zoneLiveData.value
        if (zone?.style == "default") {
            zone.content.forEach {
                if (it.type == "toolkit") {
                    for (tool in it.toolkit!!) {
                        toolkitIdList.add(tool.id ?: "")
                    }
                }
                if (it.type == "community_column_content") {
                    for (answer in it.answer!!) {
                        answerIdList.add(answer.id)
                    }
                }
            }
        }

        val requestMap = hashMapOf<String, List<String>>()
        requestMap["game"] = arrayListOf(game?.id ?: "game id is not supposed to be empty")
        requestMap["toolkit"] = toolkitIdList
        requestMap["libao"] = libaoIdList
        requestMap["community_column_content"] = answerIdList

        api.getUserRelatedInfoByPost(UserManager.getInstance().userId, requestMap.toRequestBody())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                unifiedUserTrendEntity = it
                emitter.onSuccess(Result.success(it))

                userRelatedInfoReceivedCallback?.invoke()
                userRelatedInfoReceivedCallback = null
            }, {
                emitter.onSuccess(Result.failure(it))

                userRelatedInfoReceivedCallback?.invoke()
                userRelatedInfoReceivedCallback = null
            })
    }

    fun postRequestSpeed() {
        newApi.postRequestSpeed(UserManager.getInstance().userId, gameId)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)
                    ToastUtils.showToast("感谢您的反馈信息，我们将尽快处理~")
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    e?.response()?.errorBody()?.let {
                        val content = JSONObject(it.string())
                        if (content.getInt("code") == 403208) {
                            ToastUtils.showToast("您已经提交过反馈信息，我们将尽快处理~")
                        }
                    }
                }
            })
    }

    fun performTabSelected(type: String) {
        tabSelectedLiveData.postValue(Event(LinkEntity(type = type)))
    }

    /**
     * 发送内容卡片点击事件
     * @param contentCardType 内容卡片类型
     * @param waitForUserRelatedResult 是否等待用户相关信息请求结果
     * @return 是否成功触发点击事件
     */
    fun performContentCardClicked(contentCardType: String, waitForUserRelatedResult: Boolean = false): Boolean {
        return if (contentCardList?.any { it.link.type == contentCardType } == true) {
            if (waitForUserRelatedResult && CheckLoginUtils.isLogin()) {
                userRelatedInfoReceivedCallback = {
                    contentCardClickedLiveData.postValue(Event(contentCardType))
                }
            } else {
                contentCardClickedLiveData.postValue(Event(contentCardType))
            }
            true
        } else {
            false
        }
    }

    fun performScrollToServer(): Boolean {
        // 如果该游戏的【内容卡片】在详情中显示类型为【游戏开服】的卡片时，触发点击类型为【游戏开服】的内容卡片进入开服日历表页面
        // 如果该游戏的【内容卡片】在详情中没有【游戏开服】的卡片时，定位到游戏详情页-详情tab-【游戏开服】组件
        return if (performContentCardClicked(ContentCardEntity.TYPE_SERVER, true)) {
            true
        } else if (detailDataListLiveData.value?.any { it.linkServer != null } == true) {
            scrollToListPositionLiveData.postValue(Event(LinkEntity(type = GameDetailData.TYPE_SERVER)))
            true
        } else {
            false
        }
    }

    fun performScrollToLibao(): Boolean {
        // 点击【礼包】内容标签，定位到游戏详情页-详情tab-【礼包】组件，如果详情tab中没有【礼包】组件但有【礼包tab】则跳转到礼包tab
        return if (detailDataListLiveData.value?.any { it.linkLibao != null } == true) {
            scrollToListPositionLiveData.postValue(Event(LinkEntity(type = GameDetailData.TYPE_LIBAO)))
            true
        } else if (gameDetailTabListLiveData.value?.data?.any { it.type == GameDetailTabEntity.TYPE_GIFT } == true) {
            performTabSelected(GameDetailTabEntity.TYPE_GIFT)
            true
        } else {
            false
        }
    }

    fun scrollToSpecificPositionOnDownloadClick(isUpdate: Boolean) {
        gameDetailSettingLiveData.value?.let {
            if (isUpdate && it.updateDownloadPositionLink != null) {
                handlePositionLink(it.updateDownloadPositionLink)
            } else if (it.downloadPositionLink != null) {
                handlePositionLink(it.downloadPositionLink)
            }
        }
    }

    private fun handlePositionLink(linkEntity: LinkEntity?) {
        linkEntity?.let {
            // game_detail_tab：指定游戏详情tab（除了【详情tab】之外的其他tab）、game_detail_component：指定游戏详情组件
            if (linkEntity.type == "game_detail_tab") {
                tabSelectedLiveData.postValue(Event(LinkEntity(link = linkEntity.link)))
            } else if (linkEntity.type == "game_detail_component") {
                tabSelectedLiveData.postValue(Event(LinkEntity(type = GameDetailTabEntity.TYPE_DETAIL)))
                scrollToListPositionLiveData.postValue(Event(LinkEntity(link = linkEntity.link ?: "")))
            }
        }
    }

    fun sendSuggestion() {
        val params = hashMapOf<String, String>()
        params["from"] = ""
        params["ghversion"] = PackageUtils.getGhVersionName()
        params["channel"] = HaloApp.getInstance().channel
        params["type"] = Build.MODEL
        params["sdk"] = Build.VERSION.SDK_INT.toString()
        params["version"] = Build.VERSION.RELEASE
        params["source"] = HaloApp.getInstance().application.getString(R.string.app_name)
        params["jnfj"] = MetaUtil.getBase64EncodedIMEI()
        params["manufacturer"] = Build.MANUFACTURER
        params["rom"] = MetaUtil.getRom().romName + " " + MetaUtil.getRom().romVersion

        params["suggestion_type"] = "游戏求更新"
        params["game_id"] = game?.id ?: ""
        params["message"] =
            "求更新：${game?.name}(${game?.getApk()?.firstOrNull()?.packageName}, ${
                game?.getApk()?.firstOrNull()?.version
            })"
        val requestBody = params.createRequestBody()
        api.postSuggestion(requestBody)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Response<ResponseBody?>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)
                    ToastUtils.showToast("感谢您的反馈信息，我们将尽快处理~")
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    e?.response()?.errorBody()?.let {
                        val content = JSONObject(it.string())
                        if (content.getInt("code") == 403208) {
                            ToastUtils.showToast("您已经提交过反馈信息，我们将尽快处理~")
                        }
                    }
                }
            })
    }

    @SuppressLint("CheckResult")
    fun postPKVote(pkId: String, isPositive: Boolean) {
        val data = mapOf(
            "result" to if (isPositive) "option1" else "option2"
        )
        newApi.postPK(pkId, data.toRequestBody())
            .compose(singleToMain())
            .subscribe({
                pkVoteResultLiveData.postValue(Event(Pair(pkId, isPositive)))
            }, {
                if (it is HttpException) {
                    val errorString = it.response().errorBody()?.string()
                    val errorEntity = errorString?.toObject<ErrorEntity>()
                    if (!errorEntity?.toast.isNullOrEmpty()) {
                        Utils.toast(getApplication(), errorEntity?.toast)
                    }
                } else {
                    Utils.toast(getApplication(), "投票失败")
                }
            })
    }

    /**
     * 获取游戏详情内容相应的用户相关信息
     */
    @SuppressLint("CheckResult")
    private fun getUserRelatedInfo() {
        provideUserRelatedInfoSingle()
            .compose(singleToMain())
            .subscribe({
                val value = it.getOrNull()
                if (it.isSuccess && value is UnifiedUserTrendEntity) {
                    val detailDataList = ArrayList(detailDataListLiveData.value ?: return@subscribe)
                    mergeUnifiedGameDetailWithUserRelatedInfo(value)
                    processLibaoData(detailDataList)
                    detailDataListLiveData.postValue((detailDataList))
                }
            }, {})
    }

    /**
     * 获取游戏详情内容相应的用户相关信息及礼包状态
     */
    @SuppressLint("CheckResult")
    fun getUserRelatedInfoAndGiftStatus() {
        Single.zip(provideUserRelatedInfoSingle(), provideGiftStatusSingle()) { t1, _ ->
            val detailDataList = detailDataListLiveData.value ?: return@zip
            val value = t1.getOrNull()
            if (t1.isSuccess && value is UnifiedUserTrendEntity) {
                mergeUnifiedGameDetailWithUserRelatedInfo(value)
                processLibaoData(detailDataList)
                detailDataListLiveData.postValue(ArrayList(detailDataList))
            } else if (value is Exception) {
                Utils.log(TAG, value.localizedMessage)
            }
        }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({}, {})
    }

    private fun mergeUnifiedGameDetailWithUserRelatedInfo(unifiedUserTrendEntity: UnifiedUserTrendEntity) {
        unifiedUserTrendEntity.game?.let {
            if (it.isEmpty()) return@let
            for (game in it) {
                meLiveData.value = game.me
                break
            }
        }

        zoneLiveData.value?.let { zoneEntity ->
            if (zoneEntity.style == "default") {
                zoneEntity.content.forEach {
                    if (it.type == "toolkit") {
                        if (unifiedUserTrendEntity.toolkit != null) {
                            for (originalToolkit in it.toolkit!!) {
                                for (toolkit in unifiedUserTrendEntity.toolkit!!) {
                                    if (originalToolkit.id == toolkit.id) {
                                        originalToolkit.me = toolkit.me
                                        break
                                    }
                                }
                            }
                        }
                    }
                    if (it.type == "community_column_content") {
                        if (unifiedUserTrendEntity.communityColumnContents != null) {
                            for (originalAnswer in it.answer!!) {
                                for (answer in unifiedUserTrendEntity.communityColumnContents!!) {
                                    if (originalAnswer.id == answer.id) {
                                        originalAnswer.me = answer.me
                                        originalAnswer.vote = answer.vote
                                        break
                                    }
                                }
                            }
                        }
                    }
                }
                zoneLiveData.value = zoneEntity
            }
        }

        libaoList?.let {
            if (unifiedUserTrendEntity.libao != null) {
                for (originalLibao in it) {
                    for (libao in unifiedUserTrendEntity.libao!!) {
                        if (originalLibao.id == libao.id) {
                            originalLibao.me = libao.me
                            break
                        }
                    }
                }
            }
        }
    }

    fun changeSubjectGame(subjectId: String, gameCount: Int, onFailure: () -> Unit) {
        val subjectPage = subjectPageMap.getOrPut(subjectId) { 1 }
        api.getSubjectGame(subjectId, subjectPage)
            .compose(observableToMain())
            .subscribe(object : Response<retrofit2.Response<JsonArray>>() {
                override fun onResponse(response: retrofit2.Response<JsonArray>?) {
                    super.onResponse(response)
                    if (response == null) return
                    val total = response.headers().get("total")?.toInt() ?: 0
                    val isHasNextPage = (total - subjectPage * 20) > 0
                    val type = object : TypeToken<ArrayList<GameEntity>>() {}.type
                    val games = GsonUtils.gson.fromJson<ArrayList<GameEntity>>(response.body()?.toJson() ?: "", type)
                    val detailDataList = ArrayList(detailDataListLiveData.value ?: return)
                    val detailEntity =
                        detailDataList.find { data -> data.recommendColumn?.columnId == subjectId } ?: return
                    val randomArray = RandomUtils.getRandomArray(gameCount, games.size)
                    detailEntity.recommendColumn?.columnGames?.run {
                        clear()
                        for (i in randomArray) {
                            add(games[i])
                        }
                    }
                    subjectPageMap[subjectId] = if (isHasNextPage) subjectPage + 1 else 1
                    detailDataListLiveData.postValue(detailDataList)
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    Utils.toast(getApplication(), "网络异常")
                    onFailure.invoke()
                }
            })
    }

    private val _lastAcctGame = MutableLiveData<AcctGameInfo?>()
    val lastAcctGame: LiveData<AcctGameInfo?> = _lastAcctGame

    private val _hasAnyRecord = MutableLiveData<Boolean>()
    val hasAnyRecord: LiveData<Boolean> = _hasAnyRecord

    fun loadRecordDataInDatabase(gameId: String) {
        accelerationRepository.getLastAcctGameInfoObservable(gameId)
            .compose(observableToMain())
            .subscribe(object : Response<List<AcctGameInfo>>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    compositeDisposable.add(d)
                }

                override fun onResponse(response: List<AcctGameInfo>?) {
                    super.onResponse(response)
                    _lastAcctGame.value = response?.firstOrNull()
                }

                override fun onFailure(e: HttpException?) {
                    super.onFailure(e)
                    _lastAcctGame.value = null
                }
            })


        accelerationRepository.getHasAnyAcctRecordObservable()
            .compose(observableToMain())
            .subscribe(
                object : Response<Boolean>() {
                    override fun onSubscribe(d: Disposable) {
                        super.onSubscribe(d)
                        compositeDisposable.add(d)
                    }

                    override fun onResponse(response: Boolean?) {
                        super.onResponse(response)
                        _hasAnyRecord.value = response ?: false
                    }

                    override fun onFailure(e: HttpException?) {
                        super.onFailure(e)
                        _hasAnyRecord.value = false
                    }
                })


    }

    override fun onCleared() {
        super.onCleared()
        compositeDisposable.clear()
    }

    fun markContentCardRedDot(contentCardEntity: ContentCardEntity) {
        SPUtils.setString(contentCardSp, RED_DOT_PREFIX + (gameId ?: "") + contentCardEntity.link.type, contentCardEntity.redDot.toString())
    }

    fun shouldShowContentCardRedDot(contentCardEntity: ContentCardEntity): Boolean {
        return SPUtils.getString(contentCardSp, RED_DOT_PREFIX + (gameId ?: "") + contentCardEntity.link.type) != contentCardEntity.redDot.toString()
    }

    class Factory(
        private val application: Application,
        private val gameId: String?,
        private val game: GameEntity?,
        private val entrance: String? = null
    ) : ViewModelProvider.NewInstanceFactory() {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return GameDetailViewModel(application, gameId, game, entrance) as T
        }
    }

    class ConcernResponse(var isConcern: Boolean = false, var isSuccess: Boolean = false)

    companion object {
        const val SP_CONTENT_CARD = "content_card"
        const val TAG = "GameDetailViewModel"
        const val RED_DOT_PREFIX = "red_dot_"

        //[已领取/已淘号/再领/再淘]的礼包置底显示
        fun sortLibaoList(libaoList: ArrayList<LibaoEntity>?) {
            if (UserManager.getInstance().isLoggedIn && !libaoList.isNullOrEmpty()) {
                val tempList = arrayListOf<LibaoEntity>()
                val iterator = libaoList.iterator()
                while (iterator.hasNext()) {
                    val entity = iterator.next()
                    if (entity.status == "linged" || entity.status == "taoed" ||
                        entity.status == "repeatLinged" || entity.status == "repeatTao"
                    ) {
                        tempList.add(entity)
                        iterator.remove()
                    }
                }
                libaoList.addAll(tempList)
            }
        }
    }
}
