package com.adealink.weparty.gift.manager

import com.adealink.frame.base.Rlt
import com.adealink.frame.data.json.froJsonErrorNull
import com.adealink.frame.data.json.toJsonErrorNull
import com.adealink.frame.frame.BaseFrame
import com.adealink.frame.log.Log
import com.adealink.frame.network.data.NetworkResDataNullError
import com.adealink.frame.network.data.PageReq
import com.adealink.weparty.App
import com.adealink.weparty.gift.data.GiftListReq
import com.adealink.weparty.gift.data.GiftResourceReq
import com.adealink.weparty.gift.datasource.local.GiftLocalService
import com.adealink.weparty.gift.datasource.remote.GiftHttpService
import com.adealink.weparty.module.gift.data.GiftInfo
import com.adealink.weparty.module.gift.data.GiftLocalGiftsEmptyError
import com.adealink.weparty.module.gift.data.GiftResource
import com.adealink.weparty.module.gift.data.TAG_GIFT
import com.adealink.weparty.module.gift.data.areContentSame
import com.adealink.weparty.module.gift.listener.IGiftListener
import com.adealink.weparty.util.PageHandler
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.collections.remove

val giftManager: IGiftManager by lazy { GiftManager() }

class GiftManager : BaseFrame<IGiftListener>(), IGiftManager {

    private val gifts = arrayListOf<GiftInfo>()
    private val copyGifts: List<GiftInfo> //必须在Dispatcher.HIGH_SERIAL中调用
        get() = arrayListOf<GiftInfo>().apply {
            for (info in gifts) {
                info.resource = giftResources[info.resId]
                add(info)
            }
        }
    private val giftIdToGiftMap = hashMapOf<String, GiftInfo>()
    private val giftResources = hashMapOf<String, GiftResource>()
    private val giftHttpService by lazy {
        App.instance.networkService.getHttpService(GiftHttpService::class.java)
    }

    override fun getGiftResource(forceNet: Boolean) {
        Log.d(TAG_GIFT, "getGiftResource, forceNet:$forceNet")
        launch {
            if (forceNet) {
                getRemoteGiftResource()
            }
        }
    }

    private suspend fun getRemoteGiftResource() {
        return withContext(this.coroutineContext) {
            val giftResources = hashMapOf<String, GiftResource>()
            val pageHandler = PageHandler(pageSize = 100)
            var version: String? = null
            while (!pageHandler.isEnd) {
                when (val result = giftHttpService.giftResource(
                    GiftResourceReq(PageReq(pageHandler.pageSize, pageHandler.currentPage), version)
                )
                ) {
                    is Rlt.Success -> {
                        val res = result.data.data
                        if (res == null) {
                            pageHandler.nextPage(null)
                            break
                        }
                        res.version?.let {
                            version = it
                        }
                        pageHandler.nextPage(res.next)
                        for (data in res.list) {
                            giftResources[data.id] = data
                        }
                    }

                    is Rlt.Failed -> {
                        Log.logRltI(TAG_GIFT, "getRemoteGiftResource fail", result)
                        break
                    }
                }
            }
            addCacheGiftResource(giftResources)
        }
    }

    /**
     * 必须在Dispatcher.HIGH_SERIAL上调用
     */
    private fun addCacheGiftResource(giftResources: Map<String, GiftResource>) {
        this.giftResources.clear()
        this.giftResources.putAll(giftResources)
    }

    override fun getGifts(roomId: String, forceNet: Boolean) {
        Log.d(TAG_GIFT, "getGifts, roomId:$roomId, forceNet:$forceNet")
        launch {
            if (forceNet) {
                val resultDef = async { getRemoteGifts(roomId) }
                val resourceDef = async { getRemoteGiftResource() }
                var result = resultDef.await()
                resourceDef.await()
                if (result is Rlt.Success) {
                    notifyGifts(copyGifts)
                    return@launch
                }

                if (gifts.isEmpty()) {
                    result = loadLocalGifts()
                    if (result is Rlt.Success) {
                        notifyGifts(copyGifts)
                        return@launch
                    }
                }

                notifyGifts(copyGifts)
                return@launch
            }

            if (gifts.isEmpty()) {
                var result = loadLocalGifts()
                if (result is Rlt.Success) {
                    notifyGifts(copyGifts)
                    return@launch
                }


                val resultDef = async { getRemoteGifts(roomId) }
                val resourceDef = async { getRemoteGiftResource() }
                result = resultDef.await()
                resourceDef.await()
                if (result is Rlt.Success) {
                    notifyGifts(copyGifts)
                    return@launch
                }
            }

            notifyGifts(copyGifts)
        }
    }

    override fun getGiftResource(resId: String): GiftResource? {
        return giftResources[resId]
    }

    private suspend fun getRemoteGifts(roomId: String): Rlt<List<GiftInfo>> {
        return withContext(this.coroutineContext) {
            when (val result = giftHttpService.giftList(GiftListReq(roomId))) {
                is Rlt.Success -> {
                    val res = result.data.data
                    if (res == null) {
                        Rlt.Failed(NetworkResDataNullError())
                    } else {
                        updateGifts(res.list)
                        Rlt.Success(res.list)
                    }
                }

                is Rlt.Failed -> result
            }
        }.apply { Log.logRltD(TAG_GIFT, "getRemoteGifts", this) }
    }

    private suspend fun loadLocalGifts(): Rlt<List<GiftInfo>> {
        return withContext(this.coroutineContext) {
            val localGifts: List<GiftInfo>? = froJsonErrorNull(GiftLocalService.gifts)
            if (localGifts.isNullOrEmpty()) {
                return@withContext Rlt.Failed(GiftLocalGiftsEmptyError())
            }

            if (gifts.isEmpty()) {
                addCacheGifts(localGifts)
            }
            notifyGifts(copyGifts)
            Rlt.Success(gifts)
        }.apply {
            Log.logRltD(TAG_GIFT, "loadLocalGifts", this)
        }
    }

    private fun addCacheGifts(gifts: List<GiftInfo>) {
        this@GiftManager.gifts.clear()
        this@GiftManager.gifts.addAll(gifts)
        for (gift in gifts) {
            this.giftIdToGiftMap[gift.id] = gift
        }
    }

    override suspend fun getGift(roomId: String, giftId: String): Rlt<GiftInfo?> {
        return withContext(this.coroutineContext) {
            var giftInfo = giftIdToGiftMap[giftId]
            if (giftInfo != null) {
                return@withContext Rlt.Success(giftInfo)
            }

            return@withContext when (val result = giftHttpService.giftList(GiftListReq(roomId))) {
                is Rlt.Success -> {
                    val giftList = result.data.data?.list ?: emptyList()
                    updateGifts(giftList)
                    Rlt.Success(giftIdToGiftMap[giftId])
                }

                is Rlt.Failed -> result
            }
        }.apply { Log.logRltD(TAG_GIFT, "getGift, giftId:$giftId", this) }
    }

    override suspend fun getGifts(roomId: String, giftIds: Set<String>): Rlt<List<GiftInfo>> {
        return withContext(this.coroutineContext) {
            val reqGiftInfoList = mutableListOf<String>()
            val resultGiftInfoList = mutableListOf<GiftInfo>()

            for (id in giftIds) {
                giftIdToGiftMap[id]?.let {
                    resultGiftInfoList.add(it)
                } ?: let {
                    reqGiftInfoList.add(id)
                }
            }
            if (resultGiftInfoList.isEmpty()) {
                return@withContext Rlt.Success(resultGiftInfoList)
            }

            return@withContext when (val result = giftHttpService.giftList(GiftListReq(roomId))) {
                is Rlt.Success -> {
                    val giftList = result.data.data?.list ?: emptyList()
                    updateGifts(giftList)
                    for (id in reqGiftInfoList) {
                        giftIdToGiftMap[id]?.let {
                            resultGiftInfoList.add(it)
                        }
                    }
                    Rlt.Success(resultGiftInfoList)
                }

                is Rlt.Failed -> result
            }
        }.apply { Log.logRltD(TAG_GIFT, "getGift, giftId:${giftIds.joinToString(separator = ",")}", this) }
    }

    private suspend fun updateGifts(
        newGifts: List<GiftInfo>,
        notify: Boolean = true
    ) {
        withContext(this.coroutineContext) {
            var changed = false
            if (newGifts.size != giftIdToGiftMap.size) {
                changed = true
            } else {
                for ((index, newGift) in newGifts.withIndex()) {
                    val oldGift = this@GiftManager.gifts.getOrNull(index)
                    if (!oldGift.areContentSame(newGift)) {
                        changed = true
                        break
                    }
                }
            }
            if (changed) {
                Log.d(TAG_GIFT, "updateGifts, gifts changed, notify")
                addCacheGifts(newGifts)
                saveLocalGifts(gifts)
                if (notify) {
                    notifyGifts(copyGifts)
                }
            }
        }
    }

    private fun notifyGifts(gifts: List<GiftInfo>) {
        dispatch {
            it.onGifts(gifts)
        }
    }

    /**
     * 必须在Dispatcher.HIGH_SERIAL上调用
     */
    private fun saveLocalGifts(gifts: List<GiftInfo>) {
        GiftLocalService.gifts = toJsonErrorNull(gifts) ?: ""
    }


}