package com.adealink.weparty.profile.manager

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.adealink.frame.base.CommonDataNullError
import com.adealink.frame.base.CommonParamError
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.locale.language.data.Language
import com.adealink.frame.locale.language.listener.ILanguageListener
import com.adealink.frame.log.Log
import com.adealink.frame.storage.cache.TimeoutLruCache
import com.adealink.weparty.App
import com.adealink.weparty.module.account.AccountModule
import com.adealink.weparty.module.profile.data.MyUserInfoData
import com.adealink.weparty.module.profile.data.UserConfigType
import com.adealink.weparty.module.profile.data.UserConfigType.Companion.NECESSARY_USER_ATTR_SET
import com.adealink.weparty.module.profile.data.UserInfo
import com.adealink.weparty.module.profile.data.UserNote
import com.adealink.weparty.module.profile.listener.IProfileListener
import com.adealink.weparty.profile.data.GetUserNoteReq
import com.adealink.weparty.profile.data.GetUserinfoReq
import com.adealink.weparty.profile.data.SetUserNoteReq
import com.adealink.weparty.profile.data.UserOnlineReq
import com.adealink.weparty.profile.datasource.remote.ProfileHttpService
import com.adealink.weparty.storage.AppPref
import kotlinx.coroutines.launch

val profileManager: IProfileManager by lazy { ProfileManager() }

class ProfileManager : BaseFrame<IProfileListener>(), IProfileManager, ILanguageListener {

    private val profileHttpService by lazy {
        App.instance.networkService.getHttpService(ProfileHttpService::class.java)
    }

    private val userInfoCache by lazy {
        TimeoutLruCache<String, UserInfo>(
            1000,
            10 * 60 * 1000
        ) //最多缓存1000个，缓存时长10分钟
    }

    private val userOnlineCache by lazy {
        TimeoutLruCache<String, MutableLiveData<Boolean>>(
            1000,
            10 * 60 * 1000
        ) //最多缓存1000个，缓存时长10分钟
    }

    private val userNoteCache by lazy {
        TimeoutLruCache<String, UserNote>(
            1000,
            10 * 60 * 1000
        ) //最多缓存1000个，缓存时长10分钟
    }

    init {
        App.instance.languageManager.addListener(this)
    }

    override fun getMyUid(): String {
        return AccountModule.uid
    }

    override fun getMyUserInfo(): UserInfo? {
        return userInfoCache[AccountModule.uid] ?: getMyUserInfoFromCache()
    }

    override suspend fun pullMyUserInfo(): Rlt<MyUserInfoData?> {
        val rlt = profileHttpService.getMyUserInfo()
        return when (rlt) {
            is Rlt.Failed -> {
                rlt
            }

            is Rlt.Success -> {
                Rlt.Success(rlt.data.data)
            }
        }
    }

    private fun getMyUserInfoFromCache(): UserInfo? {
        val userInfoJsonString = AppPref.userInfo
        if (userInfoJsonString.isNotEmpty()) {
            return froJsonErrorNull<UserInfo>(userInfoJsonString)
        }
        return null
    }

    override fun setMyUserInfo(userInfo: UserInfo, attrSet: Set<UserConfigType>?) {
        updateUserInfoCache(userInfo.uid, userInfo, attrSet)
        getMyUserInfo()?.let { myUserInfo ->
            toJsonErrorNull(myUserInfo)?.let {
                AppPref.userInfo = it
            }
        }
    }

    override fun updateMyUserCountryInfo(country: String, region: String, countryFlag: String) {
        val myUserInfo = getMyUserInfo() ?: return
//        myUserInfo.country = country
//        myUserInfo.region = region
//        myUserInfo.flag = countryFlag
        setMyUserInfo(myUserInfo, null)
    }

    override fun getCacheUserInfo(uid: String): UserInfo? {
        return userInfoCache[uid]
    }

    override fun updateCacheUserInfo(userInfo: UserInfo) {
        updateUserInfoCache(userInfo.uid, userInfo)//5
    }

    override suspend fun getUserInfoByUid(
        uid: String,
        cache: Boolean,
        attrSet: Set<UserConfigType>?
    ): Rlt<UserInfo> {
        if (uid.isEmpty()) {
            return Rlt.Failed(CommonParamError("uid($uid) is invalid"))
        }

        if (cache) {
            val userInfo = if (uid == getMyUid()) {
                getMyUserInfo()
            } else {
                userInfoCache[uid]
            }
            if (userInfo != null && userInfo.isCacheValid(attrSet)) {
                return Rlt.Success(userInfo)
            }
        }

        val reqAttrSet = mutableSetOf<UserConfigType>().apply {
            addAll(NECESSARY_USER_ATTR_SET)
            attrSet?.let {
                addAll(it)
            }
        }
        Log.d(
            "TAG_PROFILE",
            "ProfileManager.getUserInfoByUid, uid:${uid}, reqAttrSet:${reqAttrSet.map { it.type }}"
        )
        return when (val result =
            profileHttpService.getUserInfosByUid(GetUserinfoReq(listOf(uid)))) {
            is Rlt.Success -> {
                val res = result.data.data ?: return Rlt.Failed(CommonDataNullError())
                val userInfo = res.list.firstOrNull()
                if (userInfo == null) {
                    return Rlt.Failed(CommonDataNullError())
                }
                if (uid == getMyUid()) {
                    setMyUserInfo(userInfo, reqAttrSet)
                } else {
                    updateUserInfoCache(uid, userInfo, reqAttrSet)
                }
                Rlt.Success(userInfo)
            }

            is Rlt.Failed -> {
                result
            }
        }
    }

    override suspend fun getUsersInfoByUid(
        uidSet: Set<String>,
        cache: Boolean,
        attrSet: Set<UserConfigType>?,
    ): Rlt<Map<String, UserInfo>> {
        val userInfoMap = hashMapOf<String, UserInfo>()
        val reqUidList = uidSet.toMutableList()
        if (cache) {
            uidSet.forEach { uid ->
                if (uid != getMyUid()) {
                    userInfoCache[uid]?.let {
                        if (it.isCacheValid(attrSet)) {
                            userInfoMap[uid] = it
                            reqUidList.remove(uid)
                        }
                    }
                } else {
                    getMyUserInfo()?.let {
                        if (it.isCacheValid(attrSet)) {
                            userInfoMap[uid] = it
                            reqUidList.remove(uid)
                        }
                    }
                }
            }
        }
        if (reqUidList.isEmpty()) {
            return Rlt.Success(userInfoMap)
        }

        val reqAttrSet = mutableSetOf<UserConfigType>().apply {
            addAll(NECESSARY_USER_ATTR_SET)
            attrSet?.let {
                addAll(it)
            }
        }
        val rlt = profileHttpService.getUserInfosByUid(GetUserinfoReq(reqUidList))
        if (rlt is Rlt.Success) {
            val getUserInfoResList = rlt.data.data?.list
            getUserInfoResList?.forEach { userInfo ->
                userInfoMap[userInfo.uid] = userInfo
                if (userInfo.uid == AccountModule.uid) {
                    setMyUserInfo(userInfo, reqAttrSet)
                } else {
                    updateUserInfoCache(userInfo.uid, userInfo, reqAttrSet)
                }
            }
        }
        return Rlt.Success(userInfoMap)
    }

    override fun isUserOnline(uid: String): Boolean {
        val liveData = getUserOnlineLiveData(uid)
        return liveData.value ?: false
    }

    override fun observeUserOnline(uid: String): LiveData<Boolean> {
        return getUserOnlineLiveData(uid)
    }

    override suspend fun isUserOnline(
        uid: String,
        cache: Boolean
    ): Rlt<Boolean> {
        val rlt = isUserOnline(setOf(uid), cache)
        return when (rlt) {
            is Rlt.Failed -> {
                rlt
            }

            is Rlt.Success -> {
                Rlt.Success(rlt.data[uid] ?: false)
            }
        }
    }

    override suspend fun isUserOnline(
        uidSet: Set<String>,
        cache: Boolean
    ): Rlt<Map<String, Boolean>> {
        val onlineMap = hashMapOf<String, Boolean>()
        val reqUidList = uidSet.toMutableList()
        if (cache) {
            uidSet.forEach { uid ->
                getUserOnlineLiveData(uid).value?.let { online ->
                    onlineMap[uid] = online
                    reqUidList.remove(uid)
                }
            }
        }
        if (reqUidList.isEmpty()) {
            return Rlt.Success(onlineMap)
        }

        val rlt = profileHttpService.isUserOnline(UserOnlineReq(reqUidList))
        if (rlt is Rlt.Success) {
            val onlineResList = rlt.data.data?.list
            val onlineStatusChanged = mutableMapOf<String, Boolean>()
            onlineResList?.forEach { onlineInfo ->
                onlineStatusChanged[onlineInfo.userNo] = onlineInfo.online
            }
            notifyUserOnlineChanged(onlineStatusChanged)
        }
        return Rlt.Success(onlineMap)
    }

    override fun updateUserOnline(uid: String, online: Boolean) {
        launch {
            notifyUserOnlineChanged(mapOf(uid to online))
        }
    }

    override fun updateUserOnline(onlineStatus: Map<String, Boolean>) {
        launch {
            notifyUserOnlineChanged(onlineStatus)
        }
    }

    override suspend fun getUserNoteByUid(
        uid: String,
        cache: Boolean
    ): Rlt<UserNote> {
        if (cache) {
            val userNote = userNoteCache[uid]
            if (userNote != null) {
                return Rlt.Success(userNote)
            }
        }
        val rlt = profileHttpService.getUserNoteBy(GetUserNoteReq(listOf(uid)))
        when (rlt) {
            is Rlt.Failed -> {
                return rlt
            }

            is Rlt.Success -> {
                val userNote = rlt.data.data?.list?.find { it.uid == uid } ?: UserNote(uid, null)
                updateUserNote(mapOf(uid to userNote))
                return Rlt.Success(userNote)
            }
        }
    }

    override suspend fun getUserNoteByUids(
        uids: List<String>,
        cache: Boolean
    ): Rlt<Map<String, UserNote>> {
        val reqUidList = mutableListOf<String>()
        val resultMap = mutableMapOf<String, UserNote>()
        if (cache) {
            for (reqUid in uids) {
                val note = userNoteCache.get(reqUid)
                if (note != null) {
                    resultMap.put(reqUid, note)
                } else {
                    reqUidList.add(reqUid)
                }
            }
        }

        if (reqUidList.isEmpty()) {
            return Rlt.Success(resultMap)
        }
        val rlt = profileHttpService.getUserNoteBy(GetUserNoteReq(reqUidList))
        when (rlt) {
            is Rlt.Failed -> {
                return rlt
            }

            is Rlt.Success -> {
                val updateCacheMap = mutableMapOf<String, UserNote>()
                rlt.data.data?.list?.forEach { note ->
                    updateCacheMap.put(note.uid, note)
                    resultMap.put(note.uid, note)
                }
                updateUserNote(updateCacheMap)
                return Rlt.Success(resultMap)
            }
        }
    }

    override fun getCacheUserNote(uid: String): UserNote? {
        return userNoteCache[uid]
    }

    private fun updateUserNote(userNotes: Map<String, UserNote>) {
        for (entry in userNotes) {
            userNoteCache.put(entry.key, entry.value)
        }
    }

    override suspend fun setUserNote(
        uid: String,
        note: String?
    ): Rlt<Any> {
        val rlt = profileHttpService.setUserNote(SetUserNoteReq(uid, note ?: ""))
        when (rlt) {
            is Rlt.Failed -> {
                //Ntd.
            }

            is Rlt.Success -> {
                userNoteCache.put(uid, UserNote(uid, note))
            }
        }
        return rlt
    }

    private fun getUserOnlineLiveData(uid: String): MutableLiveData<Boolean> {
        var livedata = userOnlineCache[uid]
        if (livedata == null) {
            livedata = MutableLiveData<Boolean>()
            userOnlineCache.put(uid, livedata)
        }
        return livedata
    }

    private fun notifyUserOnlineChanged(onlineStatus: Map<String, Boolean>) {
        launch {
            for (entry in onlineStatus) {
                val livedata = getUserOnlineLiveData(entry.key)
                livedata.postValue(entry.value)
            }
            dispatch {
                it.onUserOnlineStatusChanged(onlineStatus)
            }
        }
    }


    private fun updateUserInfoCache(
        uid: String,
        userInfo: UserInfo,
        attrSet: Set<UserConfigType>? = null
    ) {
        userInfoCache.put(uid, userInfo)
    }

    override fun clear() {
        AppPref.userInfo = ""
    }

    override fun onLanguageChanged(currentLanguage: Language) {
        userInfoCache.evictAll()
    }

}
