package com.gh.gamecenter.home.custom

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.filter.RegionSettingHelper
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.observableToMain
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.home.test_v2.HomeGameTestV2DownloadStateUpdateHelper
import com.gh.gamecenter.retrofit.RetrofitManager
import com.lightgame.utils.Utils
import retrofit2.HttpException

/**
 * description : 自定义页面-新游开测 item 的分页加载 ViewModel
 */

class CustomNewGameTestUseCase(private val linkId: String) : HomeGameTestV2DownloadStateUpdateHelper {

    companion object {
        private const val TAG = "HomeGameTestV2ViewModel"

        //每页加载数据条数
        private const val PAGE_SIZE = 36

        //提前多少加载数据
        private const val LOAD_PRE = 24

        //每列条目数
        private const val COLUMN_COUNT = 3
    }

    override var mParentPosition: Int = 0
    override var mOnGameListAddCallback: (Int, List<GameEntity>) -> Unit = { _, _ -> }

    //埋点上报的信息，别的板块使用修改这里
    var location: String = "首页"
    var blockData: SubjectRecommendEntity? = null


    private val mApi = RetrofitManager.getInstance().newApi

    /**
     * 加载状态livedata
     */
    private val mLoadStateLiveData = MutableLiveData<LoadStatus>()

    /**
     * 加载数据返回livedata
     */
    private val mDataLiveData = MutableLiveData<List<GameDataWrapper>>()

    /**
     * 时间线节点事件通知livedata
     */
    private val mTimePointLiveData = MutableLiveData<String>()

    /**
     * 游戏列表滑动到指定position位置通知
     */
    private val mGameListScrollLiveData = MutableLiveData<Int>()

    /**
     * 时间轴数据
     */
    private var mTimePoint: List<StartPoint>? = null

    /**
     * 记录过滤掉的游戏
     */
    private val mFilterGameIdSet = mutableSetOf<String>()

    /**
     * 设置时间轴数据
     */
    fun setTimePoint(timePoint: List<StartPoint>, defaultPosition: Int) {
        this.mTimePoint = timePoint

        if (defaultPosition != -1) {
            mTimePointLiveData.value = timePoint.getOrNull(defaultPosition)?.timeType ?: ""
        } else {
            // 时间轴默认定位至“今天”
            // 如果今天没有开测游戏，则定位至距离今天最近的未来时间开测的游戏
            // 如果未来时间没有开测游戏，则定位至距离今天最近的过去时间开测的游戏
            val todayPosition = timePoint.indexOfFirst { it.timeType == TimeUtils.getToday() }
            val firstFutureDayPosition = timePoint.indexOfFirst { TimeUtils.getDaysOffset(TimeUtils.getDateTimeStamp(it.timeType) / 1000) > 0 }
            if (todayPosition != -1) {
                mTimePointLiveData.value = timePoint.getOrNull(todayPosition)?.timeType ?: ""
            } else if (firstFutureDayPosition != -1) {
                mTimePointLiveData.value = timePoint.getOrNull(firstFutureDayPosition)?.timeType ?: ""
            } else {
                mTimePointLiveData.value = timePoint.lastOrNull()?.timeType ?: ""
            }
        }
    }

    /**
     * 设置时间轴节点游戏总数
     * 用占位和空条目填充数据列表
     */
    fun setDataCount(dataCount: List<DataCount>) {
        if (dataCount.isEmpty()) return
        val rawCount = COLUMN_COUNT
        val gameList = mutableListOf<GameDataWrapper>()
        var index = 0
        dataCount.forEach {
            val (timeType, count) = it
            repeat(count) {
                gameList.add(GameDataWrapper(index++, timeType = timeType, isPlaceHolder = true))
            }
            val limit = rawCount - (count % rawCount)
            repeat(limit % rawCount) {
                gameList.add(GameDataWrapper(index++, timeType = timeType, isSpace = true))
            }
        }
        mDataLiveData.value = gameList
    }

    /**
     * 分页加载方向，
     * action：
     * left(左滑加载，即手指从右往左滑，也就是下一页)、
     * right(右滑加载，即手指从左往右滑，也就是上一页)
     */
    enum class Action(val command: String) {
        NEXT("next"),
        PREV("prev");

        companion object {
            fun getActionByCommand(command: String): Action {
                return values().find { it.command == command } ?: NEXT
            }
        }
    }

    /**
     * 分页加载
     * @param isRefresh 是否是刷新
     * @param action 分页加载方向
     * @param pageId 分页id ，加载下一页为上一页最后一个id，加载上一页为下一页第一个id
     * @param timeType 时间轴类型 time_type（recommend：推荐、xxxx-xx-xx：全部（比如：2023-01-01））
     * @param startId //时间轴id，起始点ID（与时间轴对应）：
     * start_id（【数据从“时间轴起始点”接口获取】，刚进入页面，或者用户自己“点击切换时间轴”的时候必填。
     * 如果是用户在数据列表里左右滑动则自动切换下一列的时候 则不用传，要用户主动切换时间轴的才传）
     */
    private fun loadData(
        linkId: String,
        isRefresh: Boolean = false,
        action: Action,
        pageId: String,
        timeType: String,
        loadTimeType: String,
        startId: String = "",
        limit: Int = PAGE_SIZE
    ) {
        Utils.log(
            TAG, "loadData: isRefresh = $isRefresh, action = ${action.command}, pageId = $pageId," +
                    "timeType = $timeType, loadTimeType = $loadTimeType, startId = $startId, limit = $limit"
        )
        if (isRefresh) {
            mLoadStateLiveData.postValue(LoadStatus.INIT_LOADING)
        } else {
            mLoadStateLiveData.postValue(LoadStatus.LIST_LOADING)
        }
        val filterQuery = mutableMapOf(
            "action" to action.command,
            "page_id" to pageId,
            "time_type" to timeType,
            "limit" to limit
        )
        if (startId.isNotEmpty()) {
            filterQuery["start_id"] = startId
        }
        mApi.getCustomPageServerTestV2(
            linkId.ifEmpty { "none" },
            filterQuery.toMap()
        ).compose(observableToMain())
            .subscribe(object : Response<List<GameData>>() {
                override fun onResponse(response: List<GameData>?) {
                    mLoadStateLiveData.postValue(LoadStatus.LIST_LOADED)
                    val data = response ?: return
                    addGameList(
                        list = data,
                        timeType = timeType,
                        pageId = pageId,
                        action = action,
                        isRefresh = false
                    )
                }

                override fun onFailure(e: HttpException?) {
                    mLoadStateLiveData.postValue(LoadStatus.INIT_FAILED)
                }
            })
    }

    fun getDataListLiveData(): LiveData<List<GameDataWrapper>> {
        return mDataLiveData
    }

    fun getLoadStateLiveData(): LiveData<LoadStatus> {
        return mLoadStateLiveData
    }

    fun getTimePointLiveData(): LiveData<String> {
        return mTimePointLiveData
    }

    fun getGameListScrollLiveData(): LiveData<Int> {
        return mGameListScrollLiveData
    }

    private fun getCurrentData(): List<GameDataWrapper> {
        return mDataLiveData.value ?: emptyList()
    }

    /**
     * 根据当前条目索引，向前和向后查找LOAD_PRE个条目，如果有占位条目，则加载数据
     */
    fun parseActionWithPosition(position: Int) {
        val dataList = getCurrentData()
        if (dataList.isEmpty()) return
        //如果当前条目是占位条目，那就前后查找有数据的借力一下
        val currentItem = dataList[position]
        if (currentItem.isPlaceHolder) {
            //当前条目是占位条目，查找前后最近的有数据的条目
            for (i in position until dataList.size) {
                val item = dataList[i]
                if (item.gameData != null && item.index - position <= LOAD_PRE) {
                    parseActionWithPosition(item.index)
                    return
                }
            }
            for (i in position downTo 0) {
                val item = dataList[i]
                if (item.gameData != null && position - item.index <= LOAD_PRE) {
                    parseActionWithPosition(item.index)
                    return
                }
            }
            return
        }

        //往前查找离position最近的占位条目
        val frontList = getCurrentData().subList(0, position + 1)
        val findFrontNearPlaceHolder = frontList.lastOrNull { it.isPlaceHolder }
        val frontPlaceHolderPosition = findFrontNearPlaceHolder?.index ?: -1
        if (findFrontNearPlaceHolder != null && position - frontPlaceHolderPosition <= LOAD_PRE) {
            //position的前LOAD_PRE个条目列表
            val list = frontList.subList(frontPlaceHolderPosition, position + 1)
            //查找holder后面的第一个有数据的条目
            val firstHasDataItem = list.firstOrNull { it.gameData != null }
            if (firstHasDataItem != null && firstHasDataItem.index > frontPlaceHolderPosition) {
                val pageId = firstHasDataItem.gameData?.id ?: return
                loadData(
                    linkId = linkId,
                    action = Action.PREV,
                    pageId = pageId,
                    timeType = firstHasDataItem.timeType,
                    loadTimeType = findFrontNearPlaceHolder.timeType
                )
            }
        }
        //往后查找离position最近的占位条目
        val behindList = getCurrentData().subList(position, getCurrentData().size)
        val findBehindPlaceHolder = behindList.firstOrNull { it.isPlaceHolder }
        val behindPlaceHolderPosition = findBehindPlaceHolder?.index ?: -1
        if (findBehindPlaceHolder != null && behindPlaceHolderPosition - position <= LOAD_PRE) {
            val list = behindList.subList(0, behindPlaceHolderPosition - position + 1)
            //查找holder前面的第一个有数据的条目
            val lastHasDataItem = list.lastOrNull { it.gameData != null }
            if (lastHasDataItem != null && lastHasDataItem.index < behindPlaceHolderPosition) {
                val pageId = lastHasDataItem.gameData?.id ?: return
                loadData(
                    linkId = linkId,
                    action = Action.NEXT,
                    pageId = pageId,
                    timeType = lastHasDataItem.timeType,
                    loadTimeType = findBehindPlaceHolder.timeType
                )
            }
        }
    }


    //添加数据到列表
    fun addGameList(
        list: List<GameData>,
        timeType: String,
        pageId: String,
        action: Action,
        isDefaultFirstDate: Boolean = true, // 是否为时间轴首个日期
        isRefresh: Boolean = false
    ) {
        val currentData = getCurrentData().toMutableList()
        val gameList = getFilterGameAndTrimList(currentData, list)
        if (currentData.size < gameList.size) {
            //出现错误，没有获取到时间轴的游戏数量, 或者数量不正确
            Utils.log(TAG, "出现错误，没有获取到时间轴的游戏数量, 或者数量不正确 ${currentData.size} < ${gameList.size}")
            return
        }
        Utils.log(
            TAG, "加载分页完成：action = $action, timeType = $timeType, pageId = $pageId, gameList = $gameList"
        )
        //给数据分配位置
        var index = currentData.indexOfFirst { it.timeType == timeType && it.isPlaceHolder }
        if (isRefresh) {
            if (index == -1) {
                Utils.log(TAG, "出现错误，没有找到 $timeType 的首个PlaceHolder $currentData")
                return
            }
            //初始化分配
            while (gameList.isNotEmpty()) {
                val gameDataWrapper = gameList.removeFirst()
                while (currentData.getOrNull(index)?.isPlaceHolder == false) {
                    index++
                }
                if (index >= currentData.size) {
                    //出现错误，没有获取到时间轴的游戏数量, 或者数量不正确
                    Utils.log(TAG, "isRefresh:出现错误，没有获取到时间轴的游戏数量, 或者数量不正确")
                    return
                }
                currentData[index] = gameDataWrapper.also { it.index = index }
            }
        } else {
            //分页加载逻辑
            if (action == Action.PREV) {
                //如果是向前加载，列表倒序一下
                gameList.reverse()
            }
            gameList.forEach {
                addGameItemWithAction(currentData, it, action)
            }
        }
        mDataLiveData.value = currentData.toMutableList()
        //如果定位不是时间轴首个日期，向前加载一页
        if (!isDefaultFirstDate) {
            parseActionWithPosition(index)
        }
    }

    /**
     * 根据条目timeType和action自动插入对应holder空位
     */
    private fun addGameItemWithAction(
        dataList: MutableList<GameDataWrapper>,
        gameDataWrapper: GameDataWrapper,
        action: Action
    ) {
        val gameTimeType = gameDataWrapper.timeType
        val insertPosition = when (action) {
            Action.NEXT -> dataList.indexOfFirst { it.timeType == gameTimeType && it.isPlaceHolder }
            Action.PREV -> dataList.indexOfLast { it.timeType == gameTimeType && it.isPlaceHolder }
        }
        val exist = dataList.find { it.timeType == gameTimeType && it.gameData?.id == gameDataWrapper.gameData?.id }
        if (exist != null) {
            Utils.log(TAG, "重复条目，不添加到列表: $exist")
            return
        }
        if (insertPosition == -1) {
            Utils.log(TAG, "出现错误，没有获取到时间轴的游戏数量, 或者数量不正确addGameItemWithAction")
            return
        }
        dataList[insertPosition] = gameDataWrapper.also { it.index = insertPosition }
    }

    /**
     * 过滤被屏蔽的游戏并清理列表中的占位，以及重新调整空白条目
     * 返回屏蔽后的游戏列表
     */
    private fun getFilterGameAndTrimList(
        dataList: MutableList<GameDataWrapper>,
        list: List<GameData>
    ): MutableList<GameDataWrapper> {
        val gameList = mutableListOf<GameDataWrapper>()
        list.forEach { gameData ->
            //记录被过滤的游戏id
            val filterGameIdList = gameData.games.map { it.id }.toMutableList()
            val gameTimeType = gameData.timeType
            val size = gameData.games.size
            //过滤被屏蔽的游戏
            val filterGames = RegionSettingHelper.filterGame(gameData.games)
            val filterGameIds = filterGames.map { it.id }
            filterGameIdList.removeAll(filterGameIds) //剩余的就是被过滤的游戏id
            mFilterGameIdSet.addAll(filterGameIdList)
            //主动过滤含有id的游戏
            filterGames.removeAll { mFilterGameIdSet.contains(it.id) }
            //过滤掉了几个游戏，裁剪列表的占位holder，并调整空白条目
            val filterMinusCount = size - filterGames.size
            trimDataWithTimeType(dataList, gameTimeType, filterMinusCount)
            //注册到外部model，用于下载按钮状态更新
            notifyParentAddGameList(filterGames)
            filterGames.forEach { gameEntity ->
                gameList.add(
                    GameDataWrapper(
                        -1, //还未分配位置的数据
                        timeType = gameTimeType,
                        gameData = gameEntity,
                        isPlaceHolder = false,
                        isSpace = false
                    )
                )
            }
        }
        return gameList
    }

    /**
     * 移除指定时间轴的count个数占位条目，
     * 调整时间轴的空白条目数量，并把后续条目的索引前移
     */
    private fun trimDataWithTimeType(
        dataList: MutableList<GameDataWrapper>,
        timeType: String,
        count: Int
    ) {
        if (count <= 0) return
        Utils.log(TAG, "过滤掉了时间轴：$timeType 上 $count 个游戏。")
        val listForTimeType = dataList.filter { it.timeType == timeType }.toMutableList()
        if (listForTimeType.isEmpty()) return
        //这个timeType还剩多少个holder占位条目
        val holderCount = listForTimeType.count { it.isPlaceHolder }
        if (holderCount < count) return
        //找到当前timeType的第一个条目索引，后续裁剪都在这个索引之后
        val timeTypeFirstIndex = listForTimeType.first().index
        //先清除这个timeType的空白条目，后续裁剪holder后再补回来
        listForTimeType.removeAll { it.isSpace }
        //删除指定个数holder
        val delListForTimeType = listForTimeType.filter { it.isPlaceHolder }.take(count)
        listForTimeType.removeAll(delListForTimeType)
        //补充空白条目
        val rawCount = COLUMN_COUNT
        val listForTimeTypeCount = listForTimeType.size
        val spaceCount = rawCount - (listForTimeTypeCount % rawCount)
        var index = timeTypeFirstIndex + listForTimeTypeCount
        repeat(spaceCount % rawCount) {
            listForTimeType.add(GameDataWrapper(index++, timeType = timeType, isSpace = true))
        }
        //替换原数组中的timeType列表
        dataList.removeAll { it.timeType == timeType }
        dataList.addAll(timeTypeFirstIndex, listForTimeType)
        //重新给当前timeType以及后面的条目设置索引
        for (i in timeTypeFirstIndex until dataList.size) {
            dataList[i].index = i
        }
    }

    /**
     * 选中时间轴，如果列表内有相关数据则滑动到指定位置
     * @param timeType 时间轴类型 time_type（recommend：推荐、xxxx-xx-xx：全部（比如：2023-01-01））
     * 还需要做时间轴联动，列表滑动，时间轴也要跟着选中对应的
     */
    fun onTimePointSelected(timeType: String) {
        mTimePointLiveData.postValue(timeType)
    }

    /**
     * 点击时间轴, 获取时间节点数据，或者移动列表到指定位置
     */
    fun onTimePointClick(timeType: String) {
        val firstItemWithTimeType = getCurrentData().find { it.timeType == timeType } ?: return
        val index = firstItemWithTimeType.index
        if (index != -1) {
            mGameListScrollLiveData.postValue(index)
            mLoadStateLiveData.postValue(LoadStatus.INIT_LOADED)
        }
        val startId = mTimePoint?.find { it.timeType == timeType }?.id ?: ""
        if (firstItemWithTimeType.isPlaceHolder) {
            //加载当前页数据 , 不足limit 则用下一页补足
            val timeTypeList = mTimePoint?.map { it.timeType }
            val currentTimeTypeIndex = timeTypeList?.indexOf(timeType)
            var loadTimeType = ""
            if (currentTimeTypeIndex != null && currentTimeTypeIndex != -1) {
                val nextTimeType = timeTypeList.getOrNull(currentTimeTypeIndex + 1)
                loadTimeType = nextTimeType ?: timeType
            }
            loadData(
                linkId = linkId,
                isRefresh = false,
                action = Action.NEXT,
                pageId = startId,
                timeType = timeType,
                loadTimeType = loadTimeType,
                startId = startId
            )
        }
    }

}