package com.gh.gamecenter.search

import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.ad.AdDelegateHelper
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.SearchActivity
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.common.baselist.LoadParams
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toArrayList
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.AdConfig
import com.gh.gamecenter.entity.SearchSubjectEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.minigame.MiniGameSearchResultRepository
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.ConcurrentHashMap
import kotlin.random.Random

class SearchGameResultViewModel(
    application: Application,
    private var mSearchKey: String?,
    private var mIsManuallySearch: Boolean,
    private val repository: ISearchGameResultRepository,
    private var mSearchType: String,
    val sourceEntrance: String
) : ListViewModel<GameEntity, SearchItemData>(application) {

    private var mPage = 0
    private var mSearchSubjects = arrayListOf<SearchSubjectEntity>()

    // 游戏广告列表，需要先本地按插入 position 排序，避免后续问题
    private var mGameSearchAdList: List<AdConfig>? = null
    private var mAdPositionSet: HashSet<Int>? = null
    private val mAdGameMap = ConcurrentHashMap<String, List<GameEntity>>()

    private var mGameEntityList = arrayListOf<GameEntity>()

    fun updateSearchKeyWithType(searchKey: String, searchType: String) {
        mSearchKey = searchKey
        mSearchType = searchType
    }

    fun clearSearchSubjects() {
        mSearchSubjects.clear()
    }

    override fun mergeResultLiveData() {
        mResultLiveData.addSource(mListLiveData) { list ->
            mGameEntityList = ArrayList(list)
            decorateListAndPost(list)
        }
    }

    @SuppressLint("CheckResult")
    private fun decorateListAndPost(list: MutableList<GameEntity>) {
        val itemDataList = ArrayList<SearchItemData>()
        val combineGameList = list.toMutableList()

        refreshWrongInstallStatus()

        repository.getSearchMiniGameCPM(mSearchKey)
            .zipWith(repository.getSearchSubject(mSearchKey, mPage)) { cpmGameList, subjectList -> // CPM游戏搜索结果列表合并
                for (cpmGame in cpmGameList) {
                    if (cpmGame.location <= 0 || cpmGame.location > list.size) {
                        combineGameList.add(cpmGame)
                    } else {
                        combineGameList.add(cpmGame.location - 1, cpmGame)
                    }
                }
                itemDataList.addAll(
                    combineGameList.mapIndexed { index, game ->
                        SearchItemData(game = game, gamePosition = index, isFirst = index == 0)
                    }
                )
                subjectList
            }
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ mutableList ->
                // 数据源来自于第三方的专题列表，包括 CPM 专题和 DSP 专题
                val thirdPartySearchSubjectList = mutableListOf<SearchSubjectEntity>()

                for (item in mutableList) {
                    // 避免同一个位置重复的专题
                    if (!mSearchSubjects.any {
                        it.location == item.location && it.columnId == item.columnId
                    }) {
                        mSearchSubjects.add(item)
                    }
                }

                mSearchSubjects.forEach {
                    if (it.type == SearchSubjectEntity.TYPE_WECHAT_GAME_CPM_COLUMN) {
                        thirdPartySearchSubjectList.add(it.apply { isWGameSubjectCPM = true })
                    } else if (it.type == SearchSubjectEntity.TYPE_DSP_GAME_COLUMN) {
                        thirdPartySearchSubjectList.add(it.apply { isDspSubject = true })
                    }
                    val item = SearchItemData(subject = it)
                    if (it.location <= 0 || it.location > itemDataList.size) {
                        itemDataList.add(item)
                    } else {
                        itemDataList.add(it.location - 1, item)
                    }
                }
                // 处理初始化列表且游戏列表size为0的情况
                handleLoadStatusWhenGameListIsEmpty(list, itemDataList)

                if (mIsManuallySearch) {
                    if (mSearchKey == AdDelegateHelper.gameSearchKeyword) {
                        updateAdConfigAndDecorateList(itemDataList, list)
                    } else {
                        AdDelegateHelper.requestAdConfig(false, mSearchKey ?: "") {
                            updateAdConfigAndDecorateList(itemDataList, list)
                        }
                    }
                } else {
                    postResultList(itemDataList, list)
                }

                if (thirdPartySearchSubjectList.isNotEmpty()) {
                    decorateListWithThirdPartyData(thirdPartySearchSubjectList, itemDataList, list)
                }
            }, {
                it.printStackTrace()
                handleLoadStatusWhenGameListIsEmpty(list, itemDataList)
                mResultLiveData.postValue(itemDataList)
            })
    }

    /**
     * 请求第三方接口数据来填充专题游戏数据，包含 DSP 专题和 CPM 专题
     */
    @SuppressLint("CheckResult")
    private fun decorateListWithThirdPartyData(
        subjects: List<SearchSubjectEntity>,
        itemDataList: ArrayList<SearchItemData>,
        list: MutableList<GameEntity>
    ) {
        val subjectList = subjects.filterNot { it.games.isNotEmpty() }
        if (subjectList.isEmpty()) return
        val subjectSingleList = subjectList.map { subject ->
            if (subject.isWGameSubjectCPM) {
                repository.getWGameCPMGameList(subject.size)
                    .onErrorReturnItem(mutableListOf())
                    .map { subject.columnId to it }
            } else {
                repository.getDspGameList(subject.columnType, subject.showDownload, subject.size)
                    .onErrorReturnItem(mutableListOf())
                    .map { subject.columnId to it }
            }
        }
        Single.zip(subjectSingleList) { it.map { item -> item as Pair<String, MutableList<GameEntity>> } }
            .map { dataList ->
                for (index in itemDataList.indices) {
                    val itemData = itemDataList[index]
                    val subject = itemData.subject ?: continue
                    if (subject.games.isNotEmpty()) continue
                    val pair = dataList.firstOrNull { data ->
                        data.first == subject.columnId
                    } ?: continue
                    val newItemData = SearchItemData(
                        subject = subject.copy(games = pair.second)
                    )
                    itemDataList[index] = newItemData
                }
                itemDataList
            }
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ dataList -> postResultList(dataList, list) }, {})
    }

    @SuppressLint("CheckResult")
    private fun updateAdConfigAndDecorateList(itemDataList: ArrayList<SearchItemData>, list: MutableList<GameEntity>) {
        mGameSearchAdList =
            AdDelegateHelper.getGameSearchAdList().filter { AdDelegateHelper.shouldShowGameSearchAd(it) }.toArrayList()
                .apply { sortBy { it.position } }

        val adPositionSet = hashSetOf<Int>()
        if (!mGameSearchAdList.isNullOrEmpty()) {
            for (config in mGameSearchAdList!!) {
                adPositionSet.add(config.position)
            }
        }
        mAdPositionSet = adPositionSet

        if (adPositionSet.isNotEmpty()) {
            val ownerAdList = arrayListOf<AdConfig>()
            val thirdPartyAdList = arrayListOf<AdConfig>()

            // 位置已经排过序，若插入位置大于列表的 size 那么直接 break 放弃
            for ((index, position) in adPositionSet.withIndex()) {
                if (position < itemDataList.size + index + 1) {
                    val adConfig = mGameSearchAdList!!.safelyGetInRelease(index)
                    val showThirdPartyAd = adConfig?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_SDK
                    val showOwnerAd = adConfig?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_OWNER
                    val showOnFailed = adConfig?.displayRule?.onFailedAction == "show"
                    if ((showThirdPartyAd && adConfig?.thirdPartyAd != null)
                        || (showOwnerAd && adConfig?.ownerAd == null && adConfig?.thirdPartyAd != null && showOnFailed)
                    ) {
                        thirdPartyAdList.add(adConfig)
                    } else if ((showOwnerAd && adConfig?.ownerAd != null)
                        || (showThirdPartyAd && adConfig?.ownerAd != null && showOnFailed)
                    ) {
                        ownerAdList.add(adConfig)
                    }
                } else {
                    break
                }
            }

            if (ownerAdList.isNotEmpty()) {
                // 存在自有广告时获取对应广告ID的游戏
                val requestSingleList = arrayListOf<Single<List<GameEntity>>>()
                ownerAdList.forEach {
                    // 没有对应广告ID的游戏数据时才添加到请求列表
                    if (mAdGameMap[it.id] == null) {
                        requestSingleList.add(createAdGameListSingle(it))
                    }
                }
                if (requestSingleList.isNotEmpty()) {
                    Single.zip(requestSingleList) {}
                        .compose(singleToMain())
                        .subscribe({
                            decorateListWithAd(itemDataList, list)
                        }, {
                            decorateListWithAd(itemDataList, list)
                        })
                } else {
                    decorateListWithAd(itemDataList, list)
                }
            } else {
                decorateListWithThirdPartyAdOnly(itemDataList, thirdPartyAdList, list)
            }
        } else {
            postResultList(itemDataList, list)
        }
    }

    fun refreshWrongInstallStatus() {
        if (mGameEntityList.isNotEmpty()) {
            PackageHelper.refreshWrongInstallStatus(mGameEntityList)
        }
    }

    private fun decorateListWithThirdPartyAdOnly(
        itemDataList: ArrayList<SearchItemData>,
        thirdPartyAdList: List<AdConfig>,
        list: List<GameEntity>
    ) {
        thirdPartyAdList.forEach {
            itemDataList.add(it.position - 1, SearchItemData(ad = it.thirdPartyAd, adConfig = it))
            SPUtils.setLong(Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + it.position, System.currentTimeMillis())
        }
        postResultList(itemDataList, list)
    }

    private fun decorateListWithAd(
        itemDataList: ArrayList<SearchItemData>,
        list: List<GameEntity>
    ) {
        val adGameOneIdSet = HashSet<String>() // 展示样式为单个游戏时记录游戏ID，避免重复
        val decoratedItemDataSize = itemDataList.size
        for ((index, position) in mAdPositionSet!!.withIndex()) {
            if (position < decoratedItemDataSize + index + 1) {
                val adConfig = mGameSearchAdList!!.safelyGetInRelease(index)
                val showThirdPartyAd = adConfig?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_SDK
                val showOwnerAd = adConfig?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_OWNER
                val showOnFailed = adConfig?.displayRule?.onFailedAction == "show"
                if ((showThirdPartyAd && adConfig?.thirdPartyAd != null)
                    || (showOwnerAd && adConfig?.ownerAd == null && adConfig?.thirdPartyAd != null && showOnFailed)
                ) {
                    itemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
                    SPUtils.setLong(
                        Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
                        System.currentTimeMillis()
                    )
                } else if ((showOwnerAd && adConfig?.ownerAd != null)
                    || (showThirdPartyAd && adConfig?.ownerAd != null && showOnFailed)
                ) {
                    val gameList = mAdGameMap[adConfig.id]
                    if (!gameList.isNullOrEmpty()) {
                        if (adConfig.ownerAd.adSource?.displayStyle == "game_zone") {
                            // 游戏专题
                            itemDataList.add(
                                position - 1,
                                SearchItemData(
                                    subject = SearchSubjectEntity(
                                        name = adConfig.ownerAd.adSource?.title ?: "",
                                        games = gameList.take(AD_SUBJECT_GAME_MAX_COUNT),
                                        adId = adConfig.id,
                                        codeId = adConfig.ownerAd.id,
                                        adIconActive = adConfig.ownerAd.adSource?.adIconActive ?: false
                                    ),
                                    adConfig = adConfig
                                )
                            )
                        } else {
                            // 单个游戏
                            var randomGameEntity = gameList.safelyGetInRelease(Random.nextInt(gameList.size))
                            if (gameList.size != 1 && adGameOneIdSet.contains(randomGameEntity?.id)) {
                                // 存在重复游戏时重新获取随机游戏
                                randomGameEntity = gameList.safelyGetInRelease(Random.nextInt(gameList.size))
                            }
                            randomGameEntity?.id?.let { adGameOneIdSet.add(it) }

                            itemDataList.add(
                                position - 1,
                                SearchItemData(game = randomGameEntity, adConfig = adConfig)
                            )
                        }
                        SPUtils.setLong(
                            Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
                            System.currentTimeMillis()
                        )
                    } else if (showOnFailed && adConfig.thirdPartyAd != null) {
                        // 自有广告为空时，显示第三方广告
                        itemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
                        SPUtils.setLong(
                            Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
                            System.currentTimeMillis()
                        )
                    }
                }
            } else {
                break
            }
        }
        postResultList(itemDataList, list)
    }

    @SuppressLint("CheckResult")
    private fun createAdGameListSingle(adConfig: AdConfig) = Single.create { emitter ->
        val pageSize = adConfig.ownerAd?.adSource?.gamesIds?.size ?: 20
        val codeId = adConfig.ownerAd?.id
        val paramsMap = mapOf("page" to "1,$pageSize", "code_id" to codeId)
        RetrofitManager.getInstance().newApi.getAdGames(adConfig.id, paramsMap)
            .compose(singleToMain())
            .subscribe({ data ->
                val filterGameList = RegionSettingHelper.filterGame(data)
                filterGameList.forEach {
                    it.run {
                        adIconActive = adConfig.ownerAd?.adSource?.adIconActive ?: false
                        adSpaceId = adConfig.id
                        gameAdSourceId = adConfig.ownerAd?.id ?: ""
                    }
                }
                mAdGameMap[adConfig.id] = filterGameList
                emitter.onSuccess(filterGameList)
            }, {
                mAdGameMap[adConfig.id] = emptyList()
                emitter.onSuccess(emptyList<GameEntity>())
            })
    }

    private fun postResultList(resultList: ArrayList<SearchItemData>, list: List<GameEntity>) {
        mResultLiveData.postValue(resultList.toMutableList())
        if (mPage == 1) {
            if (repository is MiniGameSearchResultRepository) {
                SensorsBridge.trackMiniGameSearchResultReturn(
                    GlobalActivityManager.getCurrentPageEntity().pageId,
                    GlobalActivityManager.getCurrentPageEntity().pageName,
                    sourceEntrance,
                    mSearchKey ?: "",
                    SearchActivity.toTrackSearchType(mSearchType),
                    list.isNotEmpty()
                )
            } else {
                SensorsBridge.trackGameSearchResultReturn(
                    GlobalActivityManager.getCurrentPageEntity().pageId,
                    GlobalActivityManager.getCurrentPageEntity().pageName,
                    sourceEntrance,
                    mSearchKey ?: "",
                    SearchActivity.toTrackSearchType(mSearchType),
                    list.isNotEmpty()
                )
            }

        }
    }

    private fun handleLoadStatusWhenGameListIsEmpty(list: List<GameEntity>, itemDataList: ArrayList<SearchItemData>) {
        if (mPage == 1 && list.isEmpty()) {
            mLoadStatusLiveData.value = if (itemDataList.isEmpty()) LoadStatus.INIT_EMPTY else LoadStatus.INIT_OVER
        }
    }

    override fun loadStatusControl(size: Int) {
        if (mCurLoadParams.loadOffset == LoadParams.DEFAULT_OFFSET) { // 初始化列表
            if (size == 0) {
//              初始化列表size为0情况放到mergeResultLiveData方法处理
            } else if (size == REQUEST_FAILURE_SIZE) {
                mLoadStatusLiveData.setValue(LoadStatus.INIT_FAILED)
            } else if (size < mOverLimitSize) { // 避免一个屏幕出现两次分页
                mLoadStatusLiveData.setValue(LoadStatus.INIT_OVER)
            } else {
                mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADED)
            }
        } else {
            if (size == REQUEST_FAILURE_SIZE) {
                mLoadStatusLiveData.setValue(LoadStatus.LIST_FAILED)
            } else if (size == 0) {
                mLoadStatusLiveData.setValue(LoadStatus.LIST_OVER)
            } else {
                mLoadStatusLiveData.setValue(LoadStatus.LIST_LOADED)
            }
        }

        if (size == REQUEST_FAILURE_SIZE) {
            mRetryParams = mCurLoadParams
        } else {
            mRetryParams = null
            mCurLoadParams.loadOffset = mCurLoadParams.loadOffset + 1 // 页数 + 1
        }
    }

    override fun provideDataObservable(page: Int): Observable<List<GameEntity>> {
        mPage = page
        return repository.getSearchGame(mSearchKey, page)
    }

    private val _refreshVipLoading = MutableLiveData<Event<Boolean>>()
    val refreshVipLoading: LiveData<Event<Boolean>> = _refreshVipLoading
    fun showRefreshVipLoading(show: Boolean) {
        _refreshVipLoading.value = Event(show)
    }

    class Factory(
        private val mApplication: Application,
        private val mSearchKey: String?,
        private val mIsManuallySearch: Boolean,
        private val repository: ISearchGameResultRepository,
        private val mSearchType: String,
        private val mSourceEntrance: String
    ) : ViewModelProvider.NewInstanceFactory() {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return SearchGameResultViewModel(
                mApplication,
                mSearchKey,
                mIsManuallySearch,
                repository,
                mSearchType,
                mSourceEntrance
            ) as T
        }
    }

    companion object {
        const val AD_SUBJECT_GAME_MAX_COUNT = 8
    }

}