package com.gh.gamecenter.servers.gametest2

import android.annotation.SuppressLint
import android.app.Application
import androidx.collection.ArrayMap
import androidx.collection.arrayMapOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.common.filter.RegionSettingHelper
import com.gh.gamecenter.common.HaloApp
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.utils.observableToMain
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.core.provider.IHandleGameResponseProvider
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.entity.GameServerTestTopGame
import com.gh.gamecenter.entity.ServerTestEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureConstants
import com.gh.gamecenter.retrofit.RetrofitManager
import com.therouter.TheRouter
import io.reactivex.Observable
import java.text.SimpleDateFormat
import java.util.*

class GameServerTestV2ListViewModel(
    application: Application,
    private val mGameServerTestV2ViewModel: GameServerTestV2ViewModel?,
    private val linkId: String = "none"
) : ListViewModel<ServerTestEntity.SliceData, GameServerTestV2ListViewModel.ItemData>(application) {

    var isLoadFirstPage = false
    val locationMap: ArrayMap<String, ArrayList<Int>> = arrayMapOf()
    private val mApi = RetrofitManager.getInstance().newApi
    private val mTimePositionArrayMap = ArrayMap<String, Int>()
    private val mCurrentPreviousAndNextWeeks = TimeUtils.getCurrentPreviousAndNextWeek()
    private val mTodayYesterdayAndTomorrowDate = TimeUtils.getTodayYesterdayAndTomorrow("MM.dd")
    private var mTopGameList: List<GameServerTestTopGame>? = null
    private var mRecentCount = 0
    private var mTodayCount = 0
    private var mFutureCount = 0

    override fun provideDataObservable(page: Int): Observable<MutableList<ServerTestEntity.SliceData>>? {
        return null
    }

    @SuppressLint("CheckResult")
    override fun load(loadType: LoadType?) {
        mRecentCount = 0
        mTodayCount = 0
        mFutureCount = 0
        isLoadFirstPage = true
        mListLiveData.value = null
        mLoadStatusLiveData.postValue(LoadStatus.INIT_LOADING)

        var categoryFilter = "" //如果选择全部类型，则传空值
        mGameServerTestV2ViewModel?.selectedCategoryFilterSet?.forEachIndexed { index, category ->
            if (index == 0) {
                categoryFilter = mGameServerTestV2ViewModel.getCategoryFilterValue(category).value
            } else {
                categoryFilter += "-${mGameServerTestV2ViewModel.getCategoryFilterValue(category).value}"
            }
        }

        Observable.zip(provideDetailObservable(categoryFilter), provideTopGamesObservable()) { t1, t2 ->
            mTopGameList = ArrayList(t2).apply {
                removeAll { RegionSettingHelper.shouldThisGameBeFiltered(it.game?.id) }
            }

            val dataList = ArrayList(t1)
            dataList.also {
                var index = 0
                while (index < it.size) {
                    val slide = it[index]
                    slide.gameList = RegionSettingHelper.filterGame(slide.gameList)
                    slide.collapsedGameList = RegionSettingHelper.filterGame(slide.collapsedGameList)
                    // 针对游戏的一些操作(过滤隐藏游戏，过滤隐藏APK，增加下载数据)
                    val handleGameResponse = TheRouter.get(IHandleGameResponseProvider::class.java)
                    if (handleGameResponse != null) {
                        slide.gameList = handleGameResponse.handleGameResponse(
                            slide.gameList,
                            mEntrance
                        ) as ArrayList<GameEntity>
                        slide.collapsedGameList = handleGameResponse.handleGameResponse(
                            slide.collapsedGameList,
                            mEntrance
                        ) as ArrayList<GameEntity>
                    }
                    //对应日期下没有游戏时，则跳过不显示对应日期
                    if (slide.gameList.isEmpty() && slide.collapsedGameList.isEmpty()) {
                        it.remove(slide)
                    } else {
                        when (slide.timeType) {
                            "recent" -> mRecentCount++
                            "future" -> mFutureCount++
                            else -> mTodayCount++
                        }
                    }
                    index++
                }
            }
        }.compose(observableToMain())
            .subscribe({
                if (it != null) {
                    loadStatusControl(it)
                    mListLiveData.postValue(it)
                }
            }, {
                mLoadStatusLiveData.postValue(LoadStatus.INIT_FAILED)
            })
    }

    private fun provideDetailObservable(categoryFilter: String) =
        mApi.getServerTestV2Detail(linkId.ifEmpty { "none" }, categoryFilter)

    @SuppressLint("CheckResult")
    private fun provideTopGamesObservable() =
        Observable.create { emitter ->
            mApi.getServerTestV2TopGames(linkId.ifEmpty { "none" })
                .compose(observableToMain())
                .subscribe({
                    emitter.onNext(it)
                }, {
                    emitter.onNext(emptyList<GameServerTestTopGame>())
                })
        }

    private fun loadStatusControl(entity: List<ServerTestEntity.SliceData>) {
        when {
            entity.isEmpty() -> {
                mLoadStatusLiveData.postValue(LoadStatus.INIT_EMPTY)
            }
            else -> {
                mLoadStatusLiveData.postValue(LoadStatus.INIT_LOADED)
            }
        }
    }

    override fun mergeResultLiveData() {
        mResultLiveData.addSource(mListLiveData) {
            transformData()
        }
    }

    private fun transformData() {
        val list = arrayListOf<ItemData>()
        val subList = arrayListOf<ItemData>()
        mTimePositionArrayMap.clear()

        // 置顶游戏固定显示在今天上方，如果今天没有数据则展示在未来上方；如果今天和未来都没有数据，则显示在近期上方
        val topGamesReadableDaysOffset = if (mTodayCount != 0) {
            GameServerTestV2ViewModel.TimeFilter.TODAY.value
        } else if (mFutureCount != 0) {
            GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value
        } else {
            GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value
        }
        var isTopGamesAdded = false

        mListLiveData.value?.forEach { slice ->
            subList.clear()

            var testTimeInHumanReadableFormat = ""
            val sdf = SimpleDateFormat("MM.dd", Locale.CHINA)
            val bigTestTime = sdf.format(slice.testTime * 1000)
            val day = mTodayYesterdayAndTomorrowDate.find { it.first == bigTestTime }
            if (day != null && day.second in -1..1) {
                testTimeInHumanReadableFormat = when (day.second) {
                    -1 -> "昨天"
                    0 -> "今天"
                    else -> "明天"
                }
            } else {
                val find = mCurrentPreviousAndNextWeeks.find { it.first == bigTestTime }
                if (find != null) {
                    testTimeInHumanReadableFormat = find.second
                }
            }

            val readableDaysOffset = when (slice.timeType) {
                "recent" -> GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value
                "future" -> GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value
                else -> GameServerTestV2ViewModel.TimeFilter.TODAY.value
            }

            if (!mTopGameList.isNullOrEmpty() && readableDaysOffset == topGamesReadableDaysOffset && !isTopGamesAdded) {
                subList.add(
                    ItemData(
                        isTopGames = true,
                        topGames = mTopGameList?.onEach { it.game?.testTime = sdf.format(slice.testTime * 1000) },
                        readableDaysOffset = readableDaysOffset
                    )
                )
                isTopGamesAdded = true
            }

            subList.add(
                ItemData(
                    bigTime = bigTestTime,
                    time = testTimeInHumanReadableFormat,
                    testTime = slice.testTime,
                    readableDaysOffset = readableDaysOffset,
                    gameCount = slice.gameList.size + slice.collapsedGameList.size
                )
            )
            for ((index, game) in slice.gameList.withIndex()) {
                game.serverType = game.test?.type ?: game.serverType
                game.brief = game.recommendText
                game.sequence = index
                game.testTime = sdf.format(slice.testTime * 1000)
                game.subPageCode = ExposureConstants.GAME_SERVER_TEST_V2

                val hasVideoOrBigImage = game.topVideo != null || game.homeSetting.image.isNotEmpty()
                val isBigImage = (mGameServerTestV2ViewModel?.bigImageMode ?: true) && hasVideoOrBigImage
                subList.add(
                    ItemData(
                        game = game,
                        testTime = slice.testTime,
                        readableDaysOffset = readableDaysOffset,
                        isBigImage = isBigImage,
                        gameCount = slice.gameList.size + slice.collapsedGameList.size
                    )
                )
            }

            if (slice.collapsedGameList.isNotEmpty()) {
                slice.collapsedGameList.forEachIndexed { index, game ->
                    game.serverType = game.test?.type ?: game.serverType
                    game.brief = game.recommendText
                    game.sequence = index
                    game.testTime = sdf.format(slice.testTime * 1000)
                }
                subList.add(
                    ItemData(
                        isCollapsed = true,
                        collapsedGames = slice.collapsedGameList,
                        testTime = slice.testTime,
                        readableDaysOffset = readableDaysOffset,
                        gameCount = slice.gameList.size + slice.collapsedGameList.size
                    )
                )
            }

            if (subList.size > 1) {
                val isCurrentDaysOffsetExist = mTimePositionArrayMap.getOrDefault(readableDaysOffset, 999) != 999
                if (!isCurrentDaysOffsetExist) {
                    mTimePositionArrayMap[readableDaysOffset] = list.size
                }
                list.addAll(subList)
            }
        }

        initLocationMap(list)
        mResultLiveData.postValue(list)
    }

    private fun initLocationMap(itemList: ArrayList<ItemData>) {
        locationMap.clear()
        var list: ArrayList<Int>?
        var i = 0
        val size: Int = itemList.size
        while (i < size) {
            itemList[i].game?.let { gameEntity ->
                if (gameEntity.getApk().size != 0) {
                    for (apk in gameEntity.getApk()) {
                        list = locationMap[apk.packageName]
                        if (list == null) {
                            list = ArrayList()
                            locationMap[apk.packageName] = list
                        }
                        list?.add(i)
                    }
                }
            }
            i++
        }
    }

    fun getReadableDaysOffset(position: Int): String {
        return mResultLiveData.value?.safelyGetInRelease(position)?.readableDaysOffset ?: ""
    }

    fun getGameCountTextByPosition(position: Int): String {
        val dataList = mResultLiveData.value
        val gameCount = dataList?.getOrNull(position)?.gameCount
        return if (gameCount != null) "$gameCount 款游戏" else ""
    }

    fun getBigDayTextByPosition(position: Int): String {
        var dayText = ""
        val dataList = mResultLiveData.value

        dataList?.let {
            for ((index, value) in dataList.withIndex()) {
                if (value.bigTime != null) {
                    dayText = value.bigTime ?: ""
                }
                if (index == position) {
                    return dayText
                }
            }
        }
        return dayText
    }

    fun getDayTextByPosition(position: Int): String {
        var dayText = ""
        val dataList = mResultLiveData.value

        dataList?.let {
            for ((index, value) in dataList.withIndex()) {
                if (value.time != null) {
                    dayText = value.time ?: ""
                }
                if (index == position) {
                    return dayText
                }
            }
        }
        return dayText
    }

    fun getItemAtPosition(position: Int): ItemData? {
        if (position < (mResultLiveData?.value?.size ?: 0)) {
            return mResultLiveData.value?.get(position)
        }
        return null
    }

    fun getPositionByTimeFilter(timeFilter: String): Int {
        val itemDataList = mResultLiveData.value ?: arrayListOf()
        var position = mTimePositionArrayMap.getOrDefault(timeFilter, -1)
        if (position >= 0) return position

        val recentCount =
            itemDataList.count { it.readableDaysOffset == GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value }
        val todayCount =
            itemDataList.count { it.readableDaysOffset == GameServerTestV2ViewModel.TimeFilter.TODAY.value }

        when (timeFilter) {
            GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value -> {
                position = mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.TODAY.value, -1)
                if (position >= 0) return position
                position =
                    mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value, -1)
                if (position >= 0) return position
            }
            GameServerTestV2ViewModel.TimeFilter.TODAY.value -> {
                position =
                    mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value, -1)
                if (position >= 0) return position
                position =
                    mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value, -1)
                if (position >= 0) return recentCount - 1
            }
            GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value -> {
                position = mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.TODAY.value, -1)
                if (position >= 0) return recentCount + todayCount - 1
                position =
                    mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value, -1)
                if (position >= 0) recentCount - 1
            }
        }

        return -1
    }

    data class ItemData(
        var bigTime: String? = null,
        var time: String? = null,
        var game: GameEntity? = null,
        var readableDaysOffset: String = "",
        var testTime: Long = 0,
        var isBigImage: Boolean = false,
        var isCollapsed: Boolean = false,
        var isTopGames: Boolean = false,
        var collapsedGames: List<GameEntity>? = null,
        var gameCount: Int = 0,
        var topGames: List<GameServerTestTopGame>? = null
    )

    class Factory(
        private val mGameServerTestV2ViewModel: GameServerTestV2ViewModel?,
        private val linkId: String = "none"
    ) : ViewModelProvider.NewInstanceFactory() {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return GameServerTestV2ListViewModel(HaloApp.getInstance(), mGameServerTestV2ViewModel, linkId) as T
        }
    }

}