package com.gh.gamecenter.amway

import android.annotation.SuppressLint
import android.app.Application
import com.gh.common.util.*
import com.gh.download.DownloadManager
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.constant.EntranceConsts
import com.gh.gamecenter.entity.AmwayCommentEntity
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.entity.RatingComment
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.home.LegacyHomeItemData
import com.gh.gamecenter.home.LegacyHomeSubjectTransformer
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.energy.EnergyBridge
import com.gh.gamecenter.retrofit.RetrofitManager
import com.lightgame.utils.Utils
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.SingleEmitter
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import retrofit2.HttpException
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
import kotlin.random.Random

class AmwayViewModel(application: Application) : ListViewModel<AmwayCommentEntity, AmwayListItemData>(application) {

    var entrance: String? = null
    var fixedTopAmwayId: String? = null

    // 置顶安利
    private var mFixedTopAmway: AmwayCommentEntity? = null

    private var mAmwaySubjectList = arrayListOf<SubjectEntity>()
    private var mFixedAmwayCommentList = arrayListOf<AmwayCommentEntity>()
    private var mCachedFixedAmwayCommentList = arrayListOf<AmwayCommentEntity>()

    private var mReuseCachedFixedAmwayList = false

    var positionAndPackageMap = HashMap<String, Int>() // key: packageName + position, value: position

    /**
     * [dumpOldData] 是否丢弃旧置顶安利列表，默认为 true
     */
    fun initData(dumpOldData: Boolean = true) {
        if (!fixedTopAmwayId.isNullOrEmpty()) {
            getFixedTopAmway()
        } else {
            getFixedAmwayList(dumpOldData)
        }
    }

    @SuppressLint("CheckResult")
    private fun getFixedTopAmway() {
        RetrofitManager.getInstance().api
            .getAmwayDetail(fixedTopAmwayId)
            .subscribeOn(Schedulers.io())
            .subscribe(object : BiResponse<AmwayCommentEntity>() {
                override fun onSuccess(data: AmwayCommentEntity) {
                    mFixedTopAmway = data
                    getFixedAmwayList(true)
                }

                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    getFixedAmwayList(true)
                }
            })
    }

    @SuppressLint("CheckResult")
    private fun getFixedAmwayList(
        dumpOldData: Boolean,
        emitter: SingleEmitter<MutableList<AmwayCommentEntity>>? = null
    ) {
        RetrofitManager.getInstance().api
            .getAmwayCommentList(1, 100)
            .subscribeOn(Schedulers.io())
            .subscribe(object : BiResponse<List<AmwayCommentEntity>>() {
                override fun onSuccess(data: List<AmwayCommentEntity>) {
                    updateFixedAmwayCommentList(ArrayList(data), dumpOldData)
                    getAmwaySubjectList(emitter)
                }

                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    mLoadStatusLiveData.postValue(LoadStatus.INIT_FAILED)
                }
            })
    }

    @SuppressLint("CheckResult")
    private fun getAmwaySubjectList(emitter: SingleEmitter<MutableList<AmwayCommentEntity>>? = null) {
        RetrofitManager.getInstance().api
            .amwaySubjectList
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : BiResponse<List<SubjectEntity>>() {
                override fun onSuccess(data: List<SubjectEntity>) {
                    // 为从启动弹窗跳转过来的 gameEntity 附上启动弹窗的相关数据供曝光数据使用 (草)
                    if (entrance == EntranceConsts.ENTRANCE_WELCOME) {
                        for (subject in data) {
                            subject.data?.let {
                                for (game in it) {
                                    game.setWelcomeDialogInfoIfAvailable()
                                }
                            }
                        }
                    }

                    mAmwaySubjectList = ArrayList(data)
                    load(LoadType.REFRESH)

                    emitter?.onSuccess(getRandomItems())
                }

                override fun onFailure(exception: Exception) {
                    super.onFailure(exception)
                    mLoadStatusLiveData.postValue(LoadStatus.INIT_FAILED)
                }
            })
    }

    override fun provideDataObservable(page: Int): Observable<MutableList<AmwayCommentEntity>>? = null

    override fun provideDataSingle(page: Int): Single<MutableList<AmwayCommentEntity>> {
        return if (page == 1) {
            getRandomDataSingle()
        } else {
            // 因为用了前100条数据作为随机的数据，所以后面的页数从6开始
            RetrofitManager.getInstance().api.getAmwayCommentList(page + 4, 20)
        }
    }

    override fun mergeResultLiveData() {
        mResultLiveData.addSource(mListLiveData) { list ->
            var subjectPosition = 0
            val itemDataList = arrayListOf<AmwayListItemData>()

            for (i in list.indices) {
                mAmwaySubjectList.forEach { subjectEntity ->
                    if (subjectEntity.position != -1 && subjectEntity.position == i) {
                        LegacyHomeSubjectTransformer.transform(
                            itemDataList as ArrayList<LegacyHomeItemData>,
                            subjectEntity,
                            subjectPosition++,
                            { AmwayListItemData() },
                            { addGamePositionAndPackage(itemDataList.size - 1, it) }
                        )
                    }
                }
                itemDataList.add(AmwayListItemData(amwayCommentItem = list[i]).apply { blockPosition = i + 1 })
            }

            mResultLiveData.postValue(itemDataList)
        }
    }

    private fun updateFixedAmwayCommentList(newList: ArrayList<AmwayCommentEntity>, dumpOldData: Boolean) {
        if (dumpOldData || mFixedAmwayCommentList.isEmpty()) {
            mFixedAmwayCommentList = newList
        } else {
            mReuseCachedFixedAmwayList = true
            for (newAmwayComment in newList) {
                for (oldAmwayComment in mFixedAmwayCommentList) {
                    if (newAmwayComment.id == oldAmwayComment.id) {
                        oldAmwayComment.comment = newAmwayComment.comment
                        break
                    }
                }
            }
        }
    }

    private fun addGamePositionAndPackage(position: Int, game: GameEntity) {
        var packages = ""
        for (apkEntity in game.getApk()) {
            packages += apkEntity.packageName
        }
        positionAndPackageMap[packages + (position)] = position
        game.gameLocation = GameEntity.GameLocation.INDEX
        game.setEntryMap(DownloadManager.getInstance().getEntryMap(game.name))
    }

    fun handleResultUpdate(comment: RatingComment) {
        updateAmwayCommentLikeCount(
            commentId = comment.id,
            isLiked = comment.me.isVoted,
            updatedLikeCount = comment.vote
        )
    }

    fun likeAmwayComment(gameId: String, commentId: String) {
        if (entrance == EntranceConsts.ENTRANCE_WELCOME) {
            LogUtils.uploadLikeFromWelcomeDialog()
        }
        RetrofitManager.getInstance().api
            .voteGameComment(gameId, commentId)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)

                    Utils.toast(getApplication(), "点赞成功")
                    updateAmwayCommentLikeCount(commentId, increaseCount = true, isLiked = true)

                    EnergyBridge.postEnergyTask("vote_game_comment", commentId)
                }

                override fun onFailure(e: HttpException?) {
                    ErrorHelper.handleError(getApplication(), e?.response()?.errorBody()?.string())
                }
            })
    }

    fun undoLikeAmwayComment(gameId: String, commentId: String) {
        RetrofitManager.getInstance().api
            .unvoteGameComment(gameId, commentId)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Response<ResponseBody>() {
                override fun onResponse(response: ResponseBody?) {
                    super.onResponse(response)

                    Utils.toast(getApplication(), "取消点赞")
                    updateAmwayCommentLikeCount(commentId, decreaseCount = true, isLiked = false)
                }

                override fun onFailure(e: HttpException?) {
                    ErrorHelper.handleError(getApplication(), e?.response()?.errorBody()?.string())
                }
            })
    }

    private fun updateAmwayCommentLikeCount(
        commentId: String,
        increaseCount: Boolean = false,
        decreaseCount: Boolean = false,
        isLiked: Boolean = false,
        updatedLikeCount: Int = -1
    ) {
        mResultLiveData.value?.let {
            for ((i, item) in it.withIndex()) {
                if (item.amwayCommentItem != null) {
                    val amwayComment = item.amwayCommentItem!!
                    if (amwayComment.comment.id == commentId) {
                        if (increaseCount) {
                            amwayComment.comment.me.isVoted = true
                            amwayComment.comment.vote++
                        }

                        if (decreaseCount) {
                            amwayComment.comment.me.isVoted = false
                            amwayComment.comment.vote--
                        }

                        if (updatedLikeCount != -1) {
                            amwayComment.comment.me.isVoted = isLiked
                            amwayComment.comment.vote = updatedLikeCount
                        }

                        it[i] = AmwayListItemData(amwayCommentItem = amwayComment)
                        break
                    }
                }
            }
            mResultLiveData.postValue(it)
        }
    }

    private fun getRandomDataSingle(): Single<MutableList<AmwayCommentEntity>> {
        return Single.create {
            if (mFixedAmwayCommentList.size == 0) {
                getFixedAmwayList(dumpOldData = true, emitter = it)
            } else if (mReuseCachedFixedAmwayList && mCachedFixedAmwayCommentList.isNotEmpty()) {
                it.onSuccess(mCachedFixedAmwayCommentList)
                mReuseCachedFixedAmwayList = false
            } else {
                it.onSuccess(getRandomItems())
            }
        }
    }

    private fun getRandomItems(): ArrayList<AmwayCommentEntity> {
        val randomItemsList = arrayListOf<AmwayCommentEntity>()
        val fixedAmwayListCopy = arrayListOf<AmwayCommentEntity>().apply { addAll(mFixedAmwayCommentList) }

        val gameIdSet = HashSet<String>()

        // 置顶(仅第一次加载)
        mFixedTopAmway?.let {
            randomItemsList.add(it)
            gameIdSet.add(it.game.id)
            mFixedTopAmway = null
        }

        while (randomItemsList.size < 20 && fixedAmwayListCopy.size != 0) {
            val randomPosition = Random.nextInt(fixedAmwayListCopy.size)
            val randomItem = fixedAmwayListCopy[randomPosition]

            if (!gameIdSet.contains(randomItem.game.id)) {
                gameIdSet.add(randomItem.game.id)
                randomItemsList.add(randomItem)
            }

            fixedAmwayListCopy.removeAt(randomPosition)
        }

        mCachedFixedAmwayCommentList = randomItemsList
        return randomItemsList
    }

}