package com.gh.gamecenter.home.custom

import android.annotation.SuppressLint
import android.app.Application
import androidx.collection.ArrayMap
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.common.util.GameUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.common.baselist.LoadStatus
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.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toObject
import com.gh.gamecenter.core.utils.RandomUtils
import com.gh.gamecenter.entity.PullDownPush
import com.gh.gamecenter.entity.RatingComment
import com.gh.gamecenter.entity.SubjectData
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.feature.entity.CustomPageTrackData
import com.gh.gamecenter.feature.entity.FloatingWindowEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.PageLocation
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.home.PageConfigure
import com.gh.gamecenter.home.custom.GamePositionAndPackageHelper.Companion.putGameWithPosition
import com.gh.gamecenter.home.custom.eventlistener.OnCustomPageEventListener
import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem
import com.gh.gamecenter.home.custom.model.CustomDspPlaceholderItem
import com.gh.gamecenter.home.custom.model.CustomPKItem
import com.gh.gamecenter.home.custom.model.CustomPageData
import com.gh.gamecenter.home.custom.model.CustomPageItem
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST
import com.gh.gamecenter.home.custom.model.CustomPageRepository
import com.gh.gamecenter.home.custom.model.CustomPluginItem
import com.gh.gamecenter.home.custom.model.CustomRecentGamesItem
import com.gh.gamecenter.home.custom.model.CustomSplitCommonContentCollectionItem
import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem
import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem
import com.gh.gamecenter.home.custom.model.CustomSubjectItem
import com.gh.gamecenter.home.custom.model.CustomWeChatMiniGamesCPMSubjectItem
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.login.user.UserRepository
import com.gh.gamecenter.login.user.UserViewModel
import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperViewModel
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import retrofit2.HttpException
import kotlin.collections.set

class CustomPageViewModel(application: Application) : AndroidViewModel(application), OnCustomPageEventListener {

    private val compositeDisposable = CompositeDisposable()

    private val newGameTestUserCaseMap = mutableMapOf<String, CustomNewGameTestUseCase>()

    fun getNewGameTestUserCase(linkId: String): CustomNewGameTestUseCase {
        return newGameTestUserCaseMap.getOrPut(linkId) {
            CustomNewGameTestUseCase(linkId).apply {
                setOnGameListAddCallback { position, gameEntities ->
                    gameEntities.forEach {
                        gamePositionAndPackageHelper.putGamePositionAndPackage(it, position)
                    }
                }
            }
        }
    }

    val commonContentCollectionUseCase = CommonContentCollectionUseCase.newInstance()

    private val repository = CustomPageRepository.newInstance()

    val gamePositionAndPackageHelper = GamePositionAndPackageHelper()

    private var _pageConfigure = PageConfigure()
    val pageConfigure: PageConfigure
        get() = _pageConfigure

    private val _customPageData = MutableLiveData<CustomPageData>()
    val customPageData: LiveData<CustomPageData> = _customPageData

    private val _dataList = MediatorLiveData<List<CustomPageItem>>().apply {
        addSource(repository.recentMiniGamesItemLiveData, ::notifyItemChanged)
        addSource(repository.recentQqMiniGamesItemLiveData, ::notifyItemChanged)
        addSource(repository.customDiscoverItemLiveData, ::notifyItemChanged)
        addSource(repository.recentGamesItemLiveData, ::notifyItemChanged)
        addSource(repository.customPluginItemLiveData, ::notifyItemChanged)
        addSource(repository.wechatMiniGameCPMItemLiveData, ::notifyWechatMiniGameCPMSubjectItemChanged)
        addSource(repository.customRecentAcceleratorItemLiveData, ::notifyItemChanged)
        addSource(repository.pkDataListLiveData, ::notifyPKDataChanged)
        addSource(repository.dspSubjectLiveData, ::notifyDspSubjectItemChanged)
    }

    val dataList: LiveData<List<CustomPageItem>> = _dataList

    private val _loadStatus = MutableLiveData<Pair<LoadStatus, Boolean>>()
    val loadStatus: LiveData<Pair<LoadStatus, Boolean>> = _loadStatus

    var refreshCount = 0

    val pullDownPushData = MutableLiveData<PullDownPush?>()
    val pageSwitchData = MutableLiveData<CustomPageData.PageSwitch>()

    private var searchToolbarTabWrapperViewModel: SearchToolbarTabWrapperViewModel? = null

    private val subjectChangedMap: ArrayMap<SubjectChanged, List<GameEntity>> = ArrayMap()

    /**
     * 微信CPM专题当前的页码记录
     */
    private val cpmSubjectChangedPageMap: ArrayMap<String, Int> = ArrayMap()

    private lateinit var _pageTracker: CustomPageTracker

    val pageTracker: CustomPageTracker
        get() = _pageTracker

    val pageLocation: PageLocation
        get() = pageTracker.pageLocation

    val pkVoteResultLiveData = MutableLiveData<Event<Pair<String, Boolean>>>()

    var shouldScrollToTop: Boolean = false

    fun init(
        pageConfigure: PageConfigure,
        searchToolbarTabWrapperViewModel: SearchToolbarTabWrapperViewModel?,
        pageLocation: PageLocation,
    ) {
        this.searchToolbarTabWrapperViewModel = searchToolbarTabWrapperViewModel
        _pageConfigure = pageConfigure
        _pageTracker = CustomPageTracker(pageLocation)
    }


    fun loadData() {
        loadFirst(false)
    }

    /**
     * 由于悬浮窗每次弹出都需要计数，这里当成一次性事件处理
     */
    private val _floatingWindows = MutableLiveData<Event<List<FloatingWindowEntity>>>()
    val floatingWindows: LiveData<Event<List<FloatingWindowEntity>>> = _floatingWindows

    private fun loadSuspendedWindow() {
        if (floatingWindows.value != null) {
            return
        }
        repository.loadSuspendedWindow(pageConfigure.pageId)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : BiResponse<List<FloatingWindowEntity>>() {
                override fun onSuccess(data: List<FloatingWindowEntity>) {
                    _floatingWindows.value = Event(data)
                }

                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    _floatingWindows.value = Event(emptyList())
                }

            }).addDisposable()
    }

    private fun loadPullDownPush() {
        if (pullDownPushData.value != null) {
            return
        }
        repository.loadPullDownPush(pageConfigure.pageId)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : BiResponse<PullDownPush>() {
                override fun onSuccess(data: PullDownPush) {
                    val isSlideBackgroundColorEnable =
                        _customPageData.value?.customsComponents?.firstOrNull()?.linkCommonCollection?.layout == CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_BANNER
                    if (data.id.isNotEmpty()) {
                        pullDownPushData.postValue(data.apply {
                            this.isSlideBackgroundColorEnable = isSlideBackgroundColorEnable
                            this.customPageId = pageLocation.pageId
                            this.customPageName = pageLocation.pageName
                            this.game?.customPageTrackData = CustomPageTrackData(pageLocation, moduleType = "下拉推送")
                        })
                    } else {
                        pullDownPushData.postValue(null)
                    }
                }

                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    pullDownPushData.postValue(null)
                }

            }).addDisposable()
    }

    fun loadFirst(isPullToRefresh: Boolean, forceLoad: Boolean = false) {
        _loadStatus.value = LoadStatus.INIT_LOADING to isPullToRefresh
        repository.loadFirstCustomPageData(pageConfigure.pageId, forceLoad)
            .map { (custom, list) ->
                Triple(custom, list, getPositionAndPackageMap(list))
            }
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : BiResponse<Triple<CustomPageData, List<CustomPageItem>, HashMap<String, Int>>>() {
                override fun onSuccess(data: Triple<CustomPageData, List<CustomPageItem>, HashMap<String, Int>>) {
                    val (custom, list, gameWithPositionMap) = data

                    val isSlideBackgroundColorEnable =
                        custom.customsComponents.firstOrNull()?.linkCommonCollection?.layout == CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_BANNER
                    searchToolbarTabWrapperViewModel?.enableSlideBackgroundColor(
                        pageConfigure.pageId,
                        isSlideBackgroundColorEnable
                    )

                    gamePositionAndPackageHelper.putAll(gameWithPositionMap)
                    pageConfigure.setCustomPageExposureSource("${custom.title}+${pageConfigure.pageId}")

                    _customPageData.value = custom
                    pageSwitchData.postValue(custom.pageSwitch)
                    _dataList.value = list
                    _loadStatus.value = if (list.isEmpty()) {
                        LoadStatus.INIT_EMPTY to isPullToRefresh
                    } else {
                        LoadStatus.INIT_LOADED to isPullToRefresh
                    }

                    if (custom.pageSwitch.suspendedWindow) {
                        loadSuspendedWindow()
                    } else {
                        _floatingWindows.value = Event(emptyList())
                    }
                    if (custom.pageSwitch.pullDownPush) {
                        loadPullDownPush()
                    } else {
                        pullDownPushData.postValue(null)
                    }
                }

                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    _loadStatus.value = LoadStatus.INIT_FAILED to isPullToRefresh

                }

            }).addDisposable()

    }

    override fun onRefresh() {
        refreshCount++
        commonContentCollectionUseCase.refreshCount = refreshCount
        NewFlatLogUtils.logHomePagePullRefresh(refreshCount)
        loadFirst(true, true)
    }

    override fun onLoadMore() {
        val status = loadStatus.value?.first ?: LoadStatus.INIT
        if (status == LoadStatus.LIST_LOADING || status == LoadStatus.INIT_LOADING || status == LoadStatus.LIST_OVER) {
            return
        }
        if (repository.isLoadingPKData) return
        _loadStatus.value = LoadStatus.LIST_LOADING to false
        repository.loadNextCustomPageData(pageConfigure.pageId)
            .map {
                it to getPositionAndPackageMap(it)
            }
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : BiResponse<Pair<List<CustomPageItem>, HashMap<String, Int>>>() {

                override fun onSuccess(data: Pair<List<CustomPageItem>, HashMap<String, Int>>) {
                    val (list, gameWithPositionMap) = data
                    gamePositionAndPackageHelper.putAll(gameWithPositionMap)
                    _loadStatus.value = if (list.isEmpty()) {
                        LoadStatus.LIST_OVER to false
                    } else {
                        LoadStatus.LIST_LOADED to false
                    }
                    if (list.isNotEmpty()) {
                        addData(list)
                    }
                }

                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    _loadStatus.value = LoadStatus.LIST_FAILED to false
                }
            }).addDisposable()
    }

    override fun onRetry() {
        loadFirst(false)
    }

    override fun onRotateRefresh(customSubjectCollectionItem: CustomSubjectCollectionItem) {
        if (customSubjectCollectionItem.isLoadedEnd) {
            notifyItemChanged(customSubjectCollectionItem.copy().apply {
                showPage = customSubjectCollectionItem.showPage + 1
                isLoading = false
                isLoadedEnd = true
                isBackToStart = true
            })
        } else {
            repository.loadCollectionContent(customSubjectCollectionItem)
                .map {
                    // 将新加载的游戏单添加到对应的游戏单合集中
                    it to getPositionAndPackageMap(customSubjectCollectionItem, it.firstOrNull()?.games)
                }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object :
                    BiResponse<Pair<List<CustomPageData.LinkColumnCollection.CustomSubjectEntity>, HashMap<String, Int>>>() {
                    override fun onSuccess(data: Pair<List<CustomPageData.LinkColumnCollection.CustomSubjectEntity>, HashMap<String, Int>>) {
                        val oldSubjectList = customSubjectCollectionItem.data.data
                        val (subjectList, gameWithPosition) = data
                        gamePositionAndPackageHelper.putAll(gameWithPosition)

                        subjectList.ifEmpty {
                            // 已加载完毕
                            if (customSubjectCollectionItem.showPage == 0 && oldSubjectList.firstOrNull()?.games.isNullOrEmpty()) {
                                // 首次加载的游戏被全部屏蔽且已加载完毕，移除该合集
                                notifyItemRemoved(customSubjectCollectionItem)
                            } else {
                                notifyItemChanged(customSubjectCollectionItem.copy().apply {
                                    showPage = customSubjectCollectionItem.showPage + 1
                                    isLoading = false
                                    isLoadedEnd = true
                                    isBackToStart = true
                                })
                            }
                            return
                        }
                        val targetSubjectList = subjectList.filter {
                            when (customSubjectCollectionItem.data.style) {
                                COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 -> {
                                    val needGameCount = customSubjectCollectionItem.data.styleSetting.rowsNum * 4
                                    it.games.size >= needGameCount
                                }

                                COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST -> it.games.size >= 3

                                else -> it.games.isNotEmpty()
                            }
                        }
                        targetSubjectList.ifEmpty {
                            customSubjectCollectionItem.loadPage++
                            onRotateRefresh(customSubjectCollectionItem)
                            return
                        }
                        val newSubjectList = oldSubjectList + targetSubjectList
                        val newColumCollection = customSubjectCollectionItem.data.copy(
                            _data = newSubjectList
                        )
                        val newItem = customSubjectCollectionItem.copy(
                            data = newColumCollection
                        ).apply {
                            showPage = customSubjectCollectionItem.showPage + 1
                            loadPage = customSubjectCollectionItem.loadPage + 1
                            isLoading = false
                            isLoadedEnd = false
                        }
                        notifyItemChanged(newItem)
                    }

                    override fun onFailure(exception: Exception) {
                        super.onFailure(exception)
                        Utils.toast(getApplication(), "网络异常")
                        val newItem = customSubjectCollectionItem.copy().apply {
                            isLoading = false
                        }
                        notifyItemChanged(newItem)
                    }
                }).addDisposable()
        }
    }

    override fun onChangeABatch(subjectEntity: SubjectEntity) =
        if (subjectEntity.isWechatColumnCPM) {
            onChangeWGameCPMABatch(subjectEntity)
        } else {
            onChangeNormalGameABatch(subjectEntity)
        }

    /**
     * 微信小游戏CPM的“换一批”功能实现
     *
     * @see <a href="https://jira.shanqu.cc/browse/GHZSCY-7167">【光环助手】CPM微信小游戏“换一批”功能优化</a>
     */
    private fun onChangeWGameCPMABatch(subjectEntity: SubjectEntity) {
        val subjectId = subjectEntity.id ?: return
        val page = cpmSubjectChangedPageMap[subjectId] ?: let {
            // 第一次点击“换一批”时，先缓存第一页的数据
            subjectChangedMap[SubjectChanged(subjectId, 1)] = subjectEntity.data
            2
        }
        val subjectChanged = SubjectChanged(subjectId, page)
        val gameList = subjectChangedMap[subjectChanged]
        if (gameList != null) {// 直接读取缓存数据
            notifyWGameCPMABatchChanged(gameList, subjectId, page)
        } else {
            repository.loadChangeSubjectWGameCPM(page, subjectEntity.size.limit, subjectEntity.onlyFee)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Response<List<GameEntity>>() {
                    override fun onResponse(response: List<GameEntity>?) {
                        if (response != null) {
                            subjectChangedMap[subjectChanged] = response
                            notifyWGameCPMABatchChanged(response, subjectId, page)
                        }
                    }

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

    private fun notifyWGameCPMABatchChanged(gameList: List<GameEntity>, subjectId: String, page: Int) {
        val nextPage: Int
        val cpmGameList = if (gameList.isEmpty()) {
            nextPage = 1// 已经到最后一页了，下一页是第一页
            subjectChangedMap[SubjectChanged(subjectId, 1)]!!
        } else {
            nextPage = page + 1
            gameList
        }
        cpmSubjectChangedPageMap[subjectId] = nextPage// 加载下一页数据
        changeSubjectCustomPageItem(subjectId, ArrayList(cpmGameList))
    }

    /**
     * 光环游戏的“换一批”功能实现
     */
    private fun onChangeNormalGameABatch(subjectEntity: SubjectEntity) {
        val subjectId = subjectEntity.id ?: return
        val subjectChanged = SubjectChanged(subjectId, 2)
        val gameList = subjectChangedMap[subjectChanged]
        if (gameList != null) {
            changeSubjectCustomPageItem(subjectId, getRandomGameList(subjectEntity.data, ArrayList(gameList)))
        } else {
            repository.loadChangeSubjectGame(subjectEntity)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Response<List<GameEntity>>() {
                    override fun onResponse(response: List<GameEntity>?) {
                        if (response != null) {
                            subjectChangedMap[subjectChanged] = response
                            onChangeABatch(subjectEntity)
                        }
                    }

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

    // 随机产生专题数据(换一换)
    private fun getRandomGameList(
        oldList: MutableList<GameEntity>?,
        sourceList: MutableList<GameEntity>,
    ): MutableList<GameEntity> {
        val resultGameList = ArrayList<GameEntity>()
        val filterGameList = GameUtils.removeDuplicateData(oldList, sourceList)//排除重复
        val indexes = RandomUtils.getRandomArray(oldList?.size ?: 0, filterGameList!!.size)
        for (index in indexes) {
            val gameEntity = filterGameList[index]
            resultGameList.add(gameEntity)
        }
        return resultGameList
    }

    private fun changeSubjectCustomPageItem(id: String, newGameList: MutableList<GameEntity>) {
        val oldData = _dataList.value as? MutableList<CustomPageItem>
        val newData = oldData?.toMutableList()
        if (!newData.isNullOrEmpty() && newGameList.isNotEmpty()) {
            // 替换当前专题游戏数据
            val position = newData.indexOfFirst {
                when (it) {
                    is CustomSubjectItem -> it.data.id == id
                    is CustomSplitSubjectItem -> it.data.id == id
                    else -> false
                }
            }
            if (position == -1) return

            val customPageItem = newData[position]
            if (customPageItem is CustomSubjectItem) {
                val newCustomPageItem =
                    customPageItem.copy(data = customPageItem.data.copy(mData = newGameList), _position = position)
                newData[position] = newCustomPageItem
                gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(newCustomPageItem, newGameList))
            }

            if (customPageItem is CustomSplitSubjectItem) {

                // 移除所有旧数据
                newData.removeAll {
                    it.componentPosition == customPageItem.componentPosition
                }

                // 构造新的 pageItem
                val newItems = (newGameList.indices step customPageItem.step).mapIndexed { index, startPosition ->
                    val copySubject = customPageItem.data.copy(mData = newGameList)
                    customPageItem.copy(
                        data = copySubject,
                        _position = index + position,
                        _componentPosition = customPageItem.componentPosition,
                        step = customPageItem.step,
                        startChildPosition = startPosition
                    ).apply {
                        splitItemCount = newGameList.size
                    }
                }

                gamePositionAndPackageHelper.putAll(
                    getPositionAndPackageMap(newItems)
                )

                // 将新的 items 添加到列表集合
                newData.addAll(position, newItems)

                // 修正集合中 item 的position 取值
                newData.forEachIndexed { index, item ->
                    item.position = index
                }
            }
            // 通知更新
            _dataList.value = newData
        }
    }

    private val _changeAppBarColorAction = MutableLiveData<Event<Int>>()
    val changAppBarColorAction: LiveData<Event<Int>> = _changeAppBarColorAction

    override fun onChangeAppBarColor(color: Int) {
        _changeAppBarColorAction.value = Event(color)
    }

    private fun addData(newData: List<CustomPageItem>) {
        val oldData = _dataList.value
        if (oldData.isNullOrEmpty()) {
            _dataList.value = newData
        } else {
            _dataList.value = oldData + newData
        }
    }

    /**
     * 通过CPM接口异步请求获取的微信小游戏数据插入
     */
    private fun notifyWechatMiniGameCPMSubjectItemChanged(result: Pair<String, List<GameEntity>>) {
        val oldData = _dataList.value ?: return
        val index = oldData.indexOfFirst {
            it is CustomWeChatMiniGamesCPMSubjectItem && it.data.id == result.first
        }
        if (index == -1) return
        val subjectItem = (oldData[index] as CustomWeChatMiniGamesCPMSubjectItem).apply {
            data.data = result.second.toMutableList()
            data.isWechatColumnCPM = true
        }
        val position = subjectItem.position
        val componentPosition = subjectItem.componentPosition
        val newData = oldData.toMutableList()
        val customPageItemList = repository.convertColumnDetailSubjectItems(
            position,
            componentPosition,
            subjectItem.link,
            subjectItem.data
        )
        if (customPageItemList.isEmpty()) return

        if (index == 0) {
            shouldScrollToTop = true
        }

        newData[index] = customPageItemList[0]
        newData.addAll(index + 1, customPageItemList.subList(1, customPageItemList.size))
        newData.forEachIndexed { pos, customPageItem ->
            customPageItem.position = pos
        }
        repository.notifyPositionChanged(newData.size)
        gamePositionAndPackageHelper.clear()
        gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(newData))
        _dataList.value = newData
    }

    /**
     *  通过 dsp 接口异步获取数据插入
     */
    private fun notifyDspSubjectItemChanged(result: Pair<String, List<GameEntity>>) {
        val (subjectId, gameList) = result
        val oldData = dataList.value ?: return
        val index = oldData.indexOfFirst {
            it is CustomDspPlaceholderItem && subjectId == it.data.id
        }
        if (index == -1) {
            return
        }
        val subjectItem = (oldData[index] as CustomDspPlaceholderItem).apply {
            data.data = gameList.toMutableList()
            data.isDspSubject = true
        }
        val position = subjectItem.position
        val componentPosition = subjectItem.componentPosition
        val newData = oldData.toMutableList()
        val customPageItemList = repository.convertColumnDetailSubjectItems(
            position,
            componentPosition,
            subjectItem.link,
            subjectItem.data
        )
        if (customPageItemList.isEmpty()) return

        if (index == 0) {
            shouldScrollToTop = true
        }
        newData[index] = customPageItemList[0]
        newData.addAll(index + 1, customPageItemList.subList(1, customPageItemList.size))
        newData.forEachIndexed { pos, customPageItem ->
            customPageItem.position = pos
        }
        repository.notifyPositionChanged(newData.size)
        gamePositionAndPackageHelper.clear()
        gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(newData))
        _dataList.value = newData
    }

    private fun notifyPKDataChanged(pkList: List<PKEntity>) {
        if (pkList.isEmpty()) return

        val oldDataList = _dataList.value ?: return
        val newDataList = ArrayList(oldDataList)
        pkList.forEach { pkEntity ->
            val index = oldDataList.indexOfFirst { it.link.link == pkEntity.id }
            if (index != -1) {
                val item = oldDataList[index]
                if (item is CustomPKItem) {
                    newDataList[index] = CustomPKItem(item.link, pkEntity, item.position, item.componentPosition)
                }
            }
        }
        _dataList.value = newDataList
    }

    private fun notifyItemRemoved(removeItem: CustomPageItem) {
        val oldData = _dataList.value ?: return
        val newData = oldData.toMutableList()
        if (removeItem.position >= 0 && removeItem.position < newData.size) {
            newData.removeAt(removeItem.position)
            // 更新position
            for (i in removeItem.position until newData.size) {
                newData[i].position--
            }
            repository.notifyPositionAdded(-1)
            gamePositionAndPackageHelper.clear()
            gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(newData))
            _dataList.value = newData
        }
    }

    private fun notifyItemChanged(newItem: CustomPageItem) {
        gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(listOf(newItem)))
        val oldData = _dataList.value ?: return
        val newData = oldData.toMutableList()
        if (newItem.position >= 0 && newItem.position < newData.size) {
            newData[newItem.position] = newItem
            _dataList.value = newData
        }
    }

    val shareHiddenNotifications: LiveData<HashMap<String, MutableSet<String>>>
        get() = repository.hiddenNotifications

    private fun getPositionAndPackageMap(list: List<CustomPageItem>): HashMap<String, Int> {
        val hashMap = hashMapOf<String, Int>()
        list.forEach { custom ->
            if (custom is CustomRecentGamesItem) {
                // 最近在玩 需要特殊处理
                custom.data.forEach { vGame ->
                    val packageName = vGame.downloadEntity.packageName
                    hashMap.put(packageName + custom.position, custom.position)
                }
            } else {
                custom.showDownloadGames.forEach {
                    hashMap.putGameWithPosition(it, custom.position)
                }
            }

        }
        return hashMap
    }

    /**
     * 临时屏蔽部分插件化游戏，避免直接修改数据源导致插件化区域整体刷新
     */
    fun temporarilyBlockGameInPluginItem(gameId: String) {
        val pluginItem = _dataList.value?.find { it is CustomPluginItem } as? CustomPluginItem
        pluginItem?.let {
            val newItem = it.copy(
                _link = it.link,
                data = it.data.filterNot { gameEntity -> gameEntity.id == gameId },
                _position = it.position,
                _componentPosition = it.componentPosition
            )
            notifyItemChanged(newItem)
        }
    }

    private fun getPositionAndPackageMap(
        customPageItem: CustomPageItem,
        gameList: List<GameEntity>?,
    ): HashMap<String, Int> {
        val hashMap = hashMapOf<String, Int>()
        if (customPageItem.shouldShowDownloadButton) {
            gameList?.forEach {
                hashMap.putGameWithPosition(it, customPageItem.position)
            }
        }
        return hashMap
    }

    private val _gameDetailDestination = MutableLiveData<Event<Pair<String, GameEntity>>>()
    val gameDetailDestination: MutableLiveData<Event<Pair<String, GameEntity>>> =
        _gameDetailDestination

    override fun navigateToGameDetailPageByGame(
        item: CustomPageItem,
        childPosition: Int,
        game: GameEntity,
        text: String,
    ) {
        _gameDetailDestination.value = Event(Pair("", game))
    }

    override fun onDownloadButtonClick(item: CustomPageItem, childPosition: Int, game: GameEntity) {

    }

    private val _subjectDestination = MutableLiveData<Event<SubjectEntity>>()
    val subjectDestination: LiveData<Event<SubjectEntity>> = _subjectDestination
    override fun navigateSubjectPage(item: CustomPageItem, subject: SubjectEntity) {
        _subjectDestination.value = Event(subject)
        NewLogUtils.logColumnPictureClick(
            "显示图集", subject.name ?: "", subject.id ?: "", subject.id ?: "", "column",
            subject.name ?: "", "", ""
        )
    }

    private val _subjectDestinationWithCustom =
        MutableLiveData<Event<Pair<CustomPageData.LinkColumnCollection.CustomSubjectEntity, SubjectData.SubjectType>>>()
    val subjectDestinationWithCustom: LiveData<Event<Pair<CustomPageData.LinkColumnCollection.CustomSubjectEntity, SubjectData.SubjectType>>> =
        _subjectDestinationWithCustom

    override fun navigateSubjectDetailPage(
        item: CustomSubjectCollectionItem,
        subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity,
    ) {
        _subjectDestinationWithCustom.value = Event(subject to item.data.subjectType)
    }

    private val _subjectCollectionDestination =
        MutableLiveData<Event<Triple<CustomSubjectCollectionItem, Int, CustomPageData.LinkColumnCollection.CustomSubjectEntity?>>>()
    val subjectCollectionDestination: LiveData<Event<Triple<CustomSubjectCollectionItem, Int, CustomPageData.LinkColumnCollection.CustomSubjectEntity?>>> =
        _subjectCollectionDestination

    override fun navigateSubjectCollectionPage(
        item: CustomSubjectCollectionItem,
        childPosition: Int,
        subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity?,
    ) {
        _subjectCollectionDestination.value = Event(Triple(item, childPosition, subject))
    }

    private val _gameListDetailDestination =
        MutableLiveData<Event<Triple<CustomSubjectCollectionItem, Int, CustomPageData.LinkColumnCollection.CustomSubjectEntity>>>()
    val gameListDetailDestination: LiveData<Event<Triple<CustomSubjectCollectionItem, Int, CustomPageData.LinkColumnCollection.CustomSubjectEntity>>> =
        _gameListDetailDestination

    override fun navigateGameListDetailPage(
        item: CustomSubjectCollectionItem,
        childPosition: Int,
        subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity,
    ) {
        _gameListDetailDestination.value = Event(Triple(item, childPosition, subject))
    }

    private val _userDestination = MutableLiveData<Event<Triple<String, String, String>>>()
    val userDestination: LiveData<Event<Triple<String, String, String>>> = _userDestination
    override fun navigateUserHomePage(item: CustomPageItem, userId: String) {
        val entrance = ""
        val path = ""
        when {
            item is CustomSubjectCollectionItem -> pageTracker.trackGameListCollectionClickWithUser(item, userId)
        }
        _userDestination.value = Event(Triple(userId, entrance, path))
    }

    private val _interestedGameDestination = MutableLiveData<Event<Unit>>()
    val interestedGameDestination: LiveData<Event<Unit>> = _interestedGameDestination
    override fun navigateToInterestedGamePage(item: CustomPageItem) {
        NewFlatLogUtils.logInterestedGameClick(pageConfigure.entrance, "")
        _interestedGameDestination.value = Event(Unit)
    }

    private val _discoverDestination = MutableLiveData<Event<Unit>>()
    val discoverDestination: LiveData<Event<Unit>> = _discoverDestination
    override fun navigateToDiscoveryPage(item: CustomPageItem) {
        NewFlatLogUtils.logDiscoverDetailPageEnter(pageConfigure.entrance, "")
        _discoverDestination.value = Event(Unit)
    }

    private val _linkDestination = MutableLiveData<Event<Pair<LinkEntity, ExposureEvent?>>>()
    val linkDestination: LiveData<Event<Pair<LinkEntity, ExposureEvent?>>> = _linkDestination
    override fun navigateToLinkPage(
        item: CustomPageItem,
        link: LinkEntity,
        text: String,
        exposureEvent: ExposureEvent?,
    ) {
        _linkDestination.value = Event(Pair(link, exposureEvent))
    }

    private val _amwayDestination = MutableLiveData<Event<Unit>>()
    val amwayDestination: LiveData<Event<Unit>> = _amwayDestination
    override fun navigateToAmway(item: CustomPageItem) {
        _amwayDestination.value = Event(Unit)
    }

    private val _badgeWallDestination = MutableLiveData<Event<RatingComment>>()
    val badgeWallDestination: LiveData<Event<RatingComment>> = _badgeWallDestination
    override fun navigateToBadgeWall(item: CustomPageItem, comment: RatingComment) {
        _badgeWallDestination.value = Event(comment)
    }

    private val _gameDetailDestinationOnAmway =
        MutableLiveData<Event<Pair<String, ExposureEvent?>>>()
    val gameDetailDestinationOnAmway: LiveData<Event<Pair<String, ExposureEvent?>>> =
        _gameDetailDestinationOnAmway

    override fun navigateToGameDetailPageById(
        item: CustomPageItem,
        gameId: String,
        gameName: String?,
        childPosition: Int,
    ) {
        val exposureEvent = item.exposureEventList.getOrNull(childPosition)
        _gameDetailDestinationOnAmway.value = Event(Pair(gameId, exposureEvent))
    }

    private val _ratingReplyDestination = MutableLiveData<Event<Pair<String, String>>>()
    val ratingReplyDestination: LiveData<Event<Pair<String, String>>> = _ratingReplyDestination
    override fun navigateToRatingReplyPage(item: CustomPageItem, gameId: String, commentId: String) {
        _ratingReplyDestination.value = Event(gameId to commentId)
    }

    private val _commonCollectionDestination =
        MutableLiveData<Event<Pair<CustomPageData.CommonContentCollection, String>>>()
    val commonCollectionDestination: LiveData<Event<Pair<CustomPageData.CommonContentCollection, String>>> =
        _commonCollectionDestination

    override fun navigateToCommonCollectionDetailPage(item: CustomPageItem, link: LinkEntity) {
        val data = when {
            item is CustomCommonContentCollectionItem -> item.data
            item is CustomSplitCommonContentCollectionItem -> item.data
            else -> null
        }
        data?.let {
            _commonCollectionDestination.value = Event(data to (link.link ?: ""))
        }
    }

    private val _gameListCollectionDestination = MutableLiveData<Event<CustomSubjectCollectionItem>>()
    val gameListCollectionDestination: LiveData<Event<CustomSubjectCollectionItem>> = _gameListCollectionDestination
    override fun navigateGameListCollectionDetailPage(item: CustomSubjectCollectionItem) {
        _gameListCollectionDestination.value = Event(item)
    }

    private val _gameListSquareDestination = MutableLiveData<Event<CustomSubjectCollectionItem>>()
    val gameListSquareDestination: LiveData<Event<CustomSubjectCollectionItem>> = _gameListSquareDestination
    override fun navigateToGameListSquare(item: CustomSubjectCollectionItem) {
        _gameListSquareDestination.value = Event(item)
    }

    @SuppressLint("CheckResult")
    fun postPKVote(pkId: String, isPositive: Boolean) {
        repository.postPKVote(pkId, isPositive)
            .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(), "投票失败")
                }
            })
    }

    fun hideNotificationItem(id: String, itemId: String) {
        repository.hideNotificationItem(id, itemId)
    }

    private fun Disposable.addDisposable() {
        compositeDisposable.add(this)
    }

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

    class SubjectChanged(
        val subjectId: String,
        val page: Int,
    ) {
        companion object {
            private const val HASH = 30
        }

        override fun hashCode(): Int {
            return subjectId.hashCode() + page.hashCode() + HASH
        }

        override fun equals(other: Any?): Boolean {
            if (other !is SubjectChanged) {
                return false
            }
            return other.subjectId == subjectId
                    && other.page == page
        }

    }

    class Factory(private val mApplication: Application)
        : ViewModelProvider.NewInstanceFactory() {

        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return CustomPageViewModel(mApplication) as T
        }

    }

}