package com.remax.visualnovel.manager.nim

import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.remax.visualnovel.R
import com.remax.visualnovel.app.base.app.CommonApplicationProxy
import com.remax.visualnovel.entity.imbean.RecentContactWrapper
import com.remax.visualnovel.entity.response.NimBean
import com.remax.visualnovel.extension.resumeWithActive
import com.remax.visualnovel.extension.toast
import com.remax.visualnovel.manager.login.LoginManager
import com.remax.visualnovel.ui.main.MainActivity
import com.remax.visualnovel.ui.wallet.manager.WalletManager
import com.google.gson.Gson
import com.netease.nimlib.sdk.NIMClient
import com.netease.nimlib.sdk.NotificationFoldStyle
import com.netease.nimlib.sdk.Observer
import com.netease.nimlib.sdk.RequestCallback
import com.netease.nimlib.sdk.StatusBarNotificationConfig
import com.netease.nimlib.sdk.msg.MsgService
import com.netease.nimlib.sdk.msg.constant.MsgStatusEnum
import com.netease.nimlib.sdk.msg.constant.NotificationExtraTypeEnum
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum
import com.netease.nimlib.sdk.msg.model.IMMessage
import com.netease.nimlib.sdk.v2.V2NIMError
import com.netease.nimlib.sdk.v2.auth.V2NIMLoginListener
import com.netease.nimlib.sdk.v2.auth.V2NIMLoginService
import com.netease.nimlib.sdk.v2.auth.enums.V2NIMLoginClientChange
import com.netease.nimlib.sdk.v2.auth.enums.V2NIMLoginStatus
import com.netease.nimlib.sdk.v2.auth.model.V2NIMKickedOfflineDetail
import com.netease.nimlib.sdk.v2.auth.model.V2NIMLoginClient
import com.netease.nimlib.sdk.v2.auth.option.V2NIMLoginOption
import com.netease.nimlib.sdk.v2.conversation.V2NIMConversationListener
import com.netease.nimlib.sdk.v2.conversation.V2NIMConversationService
import com.netease.nimlib.sdk.v2.conversation.enums.V2NIMConversationType
import com.netease.nimlib.sdk.v2.conversation.model.V2NIMConversation
import com.netease.nimlib.sdk.v2.conversation.params.V2NIMConversationFilter
import com.netease.nimlib.sdk.v2.message.V2NIMMessage
import com.netease.nimlib.sdk.v2.message.V2NIMMessageService
import com.netease.nimlib.sdk.v2.message.config.V2NIMMessageConfig
import com.netease.nimlib.sdk.v2.message.params.V2NIMSendMessageParams
import com.netease.nimlib.sdk.v2.user.V2NIMUserService
import com.netease.nimlib.sdk.v2.utils.V2NIMConversationIdUtil
import com.pengxr.modular.eventbus.generated.events.EventDefineOfUserEvents
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import timber.log.Timber


/**
 * Created by HJW on 2020/9/28
 */
object NimManager : DefaultLifecycleObserver {
    //没钱发送消息
    const val SEND_IM_INSUFFICIENT_BALANCE = 20000

    //发消息等级不足
    const val SEND_IM_LEVEL_ERROR = 20001

    var account = ""
        private set

    fun log(content: String) {
        Timber.i("云信Manager $content")
    }

    override fun onCreate(owner: LifecycleOwner) {
        addLoginListener(true)
    }

    override fun onDestroy(owner: LifecycleOwner) {
        _conversationSyncLiveData.value = false
        _updateLiveData.value = null
        _deleteLiveData.value = null
        _addLiveData.value = null
        addLoginListener(false)
        addConversationListener(false)
    }

    private val loginListener = object : V2NIMLoginListener {
        override fun onLoginStatus(status: V2NIMLoginStatus?) {
            when (status) {
                V2NIMLoginStatus.V2NIM_LOGIN_STATUS_LOGINED -> {
                    log("已登录")
                    addConversationListener(true)
                }

                V2NIMLoginStatus.V2NIM_LOGIN_STATUS_LOGOUT -> {
                    log("已登出")
                    addConversationListener(false)
                }

                else -> {
                    log("登录状态回调 $status")
                }
            }
        }

        override fun onLoginFailed(error: V2NIMError?) {
            addConversationListener(false)
            log("onLoginFailed ${error?.code} ${error?.desc}")
        }

        override fun onKickedOffline(detail: V2NIMKickedOfflineDetail?) {
            LoginManager.logout()
        }

        override fun onLoginClientChanged(
            change: V2NIMLoginClientChange?,
            clients: List<V2NIMLoginClient?>?
        ) {

        }
    }

    private val v2NIMConversationService by lazy {
        NIMClient.getService(V2NIMConversationService::class.java)
    }

    private val v2NIMLoginService by lazy {
        NIMClient.getService(V2NIMLoginService::class.java)
    }

    private val v2MessageService by lazy {
        NIMClient.getService(V2NIMMessageService::class.java)
    }


    /**
     * 监听云信登录状态
     */
    fun addLoginListener(register: Boolean) {
        if (register) {
            v2NIMLoginService.addLoginListener(loginListener)
        } else {
            v2NIMLoginService.removeLoginListener(loginListener)
        }
    }

    /**
     * 注册监听会话列表
     */
    private fun addConversationListener(register: Boolean) {
        if (register) {
            v2NIMConversationService.addConversationListener(conversationListener)
        } else {
            v2NIMConversationService.removeConversationListener(conversationListener)
        }
    }

    private val _conversationSyncLiveData = MutableLiveData<Boolean>(false)
    val conversationSyncLiveData: LiveData<Boolean> = _conversationSyncLiveData

    // 会话变化LiveData，用于通知会话信息变化变更
    private val _updateLiveData = MutableLiveData<List<RecentContactWrapper>?>()
    val updateLiveData: LiveData<List<RecentContactWrapper>?> = _updateLiveData

    // 删除会话LiveData，用于通知会话删除结果
    private val _deleteLiveData = MutableLiveData<List<String>?>()
    val deleteLiveData: LiveData<List<String>?> = _deleteLiveData

    // 创建会话
    private val _addLiveData = MutableLiveData<RecentContactWrapper?>()
    val addLiveData: LiveData<RecentContactWrapper?> = _addLiveData

    private val conversationListener = object : V2NIMConversationListener {
        /**
         * 数据同步开始回调。
         */
        override fun onSyncStarted() {

        }

        /**
         * 数据同步结束回调。如果数据同步已开始，建议在数据同步结束后再进行其他会话操作。
         */
        override fun onSyncFinished() {
            log("可以开始拉会话列表")
            EventDefineOfUserEvents.onUserUnReadChanged().post(null)
            MainScope().launch {
                _conversationSyncLiveData.value = true
            }
        }

        /**
         * 数据同步失败回调。
         */
        override fun onSyncFailed(error: V2NIMError?) {

        }

        /**
         *  会话成功创建回调。
         */
        override fun onConversationCreated(conversation: V2NIMConversation) {
            log("会话成功创建 $conversation")
            _addLiveData.value = RecentContactWrapper(conversation)
        }

        /**
         * 主动删除会话回调。
         */
        override fun onConversationDeleted(conversationIds: List<String>) {
            log("会话被删除 $conversationIds")
            _deleteLiveData.value = conversationIds
        }

        /**
         * 会话变更回调。当置顶会话、会话有新消息、主动更新会话成功时会触发该回调。
         */
        override fun onConversationChanged(conversationList: List<V2NIMConversation>) {
            log("会话变更回调 $conversationList")
            _updateLiveData.value = conversationList.map { RecentContactWrapper(it) }
        }

        /**
         * 会话消息总未读数变更回调。
         */
        override fun onTotalUnreadCountChanged(unreadCount: Int) {
            log("会话消息总未读数变更 $unreadCount")
            EventDefineOfUserEvents.onUserUnReadChanged().post(null)
        }

        /**
         * 过滤后的未读数变更回调。调用 subscribeUnreadCountByFilter 方法订阅监听后，当会话过滤后的未读数变化时会返回该回调。
         */
        override fun onUnreadCountChangedByFilter(
            filter: V2NIMConversationFilter?,
            unreadCount: Int
        ) {

        }

        /**
         * 同一账号多端登录后的会话已读时间戳标记的回调。
         */
        override fun onConversationReadTimeUpdated(conversationId: String?, readTime: Long) {

        }
    }

    /**
     * 登录云信
     */
    fun login(nimBean: NimBean) {
        v2NIMLoginService.login(
            nimBean.accountId, nimBean.token, V2NIMLoginOption().apply {
                retryCount = 3
            }, {
                log("login调用成功")
                account = nimBean.accountId
                loginSuccess()
            },
            { error ->
                val code = error.code
                val desc = error.desc
                log("login调用失败 onFailed errorCode:$code  desc:$desc")
            })
    }


    fun logout() {
        // 请勿在 Activity 的 `onDestroy` 中调用 `logout` 方法
        v2NIMLoginService.logout({
            log("logout调用成功")
            LoginManager.contactUnreadCount = 0
        }, { error ->
            val code = error.code
            val desc = error.desc
            log("logout调用失败 onFailed errorCode:$code  desc:$desc")
        })
    }

    private var offset = 0L

    /**
     * 分页获取所有会话列表
     */
    data class Conversation(
        val list: List<V2NIMConversation>,
        val hasMore: Boolean
    )

    var allConversation: MutableList<RecentContactWrapper>? = null

    private var queryConversationStart = false

    fun getUserList(accountIds: List<String?>?) {
        NIMClient.getService(V2NIMUserService::class.java).getUserList(accountIds?.filterNotNull(), {
            log("获取用户信息成功 $accountIds")
        }) {
            log("获取用户信息失败 onFailed errorCode:${it.code}  desc:$${it.desc}")
        }
    }

    suspend fun getConversationList(isRefresh: Boolean) =
        suspendCancellableCoroutine { coroutine ->
            if (queryConversationStart) {
                log("queryConversation,has Started return")
            }
            queryConversationStart = true
            coroutine.invokeOnCancellation {
                coroutine.resumeWithActive(Conversation(emptyList(), false))
                queryConversationStart = false
            }
            if (isRefresh) {
                allConversation = null
                offset = 0L
            }
            val pageLimit = 100
            v2NIMConversationService.getConversationList(offset, pageLimit, {
                offset = it.offset
                val conversationList = it.conversationList
                val hasMore = conversationList.size == pageLimit
                log("拉到的会话列表 $conversationList")
                queryConversationStart = false
                coroutine.resumeWithActive(Conversation(conversationList, hasMore))
            }) {
                queryConversationStart = false
                log("拉取会话列表失败 onFailed errorCode:${it.code}  desc:$${it.desc}")
                coroutine.resumeWithActive(Conversation(emptyList(), true))
            }
        }

    /**
     * 获取会话
     * @param conversationId 会话 ID
     */
    fun getConversation(conversationId: String, success: () -> Unit, error: (errorCode: Int) -> Unit) {
        v2NIMConversationService.getConversation(conversationId, { conversation ->
            success.invoke()
            log("获取会话${conversationId}成功:  $conversation")
        }) {
            error.invoke(it.code)
            log("获取会话${conversationId}失败 onFailed errorCode:${it.code}  desc:$${it.desc}")
        }
    }

    /**
     * 创建会话
     */
    fun createConversation(accountId: String?, success: () -> Unit, error: (errorCode: Int) -> Unit) {
        val conversationId = V2NIMConversationIdUtil.p2pConversationId(accountId)
        v2NIMConversationService.createConversation(conversationId, { conversation ->
            log("创建会话成功:  $conversation")
            success.invoke()
        }) {
            error.invoke(it.code)
            log("创建${conversationId}会话失败accountId:$accountId onFailed errorCode:${it.code}  desc:$${it.desc}")
        }
    }

    /**
     * 获取登录连接状态
     * IM 登录连接状态表示当前登录的 NIM SDK 实例与网易云信服务端的长连接状态，也可以理解为用户客户端和网易云信服务端的网络连接状态。
     * V2NIM_CONNECT_STATUS_DISCONNECTED(0)	SDK 未连接服务端
     * V2NIM_CONNECT_STATUS_CONNECTED(1)	SDK 已连接服务端
     * V2NIM_CONNECT_STATUS_CONNECTING(2)	SDK 正在与服务端连接
     * V2NIM_CONNECT_STATUS_WAITING(3)	SDK 正在等待与服务端重连
     */
    fun getConnectStatus() = v2NIMLoginService.connectStatus

    val isLogin: Boolean
        get() = v2NIMLoginService.loginStatus == V2NIMLoginStatus.V2NIM_LOGIN_STATUS_LOGINED

    private fun loginSuccess() {
        log("loginSuccess 当前状态：${v2NIMLoginService.loginStatus}")
        when (v2NIMLoginService.loginStatus) {
            V2NIMLoginStatus.V2NIM_LOGIN_STATUS_LOGINED -> {
                addConversationListener(true)
            }

            else -> {

            }
        }

        //是否需要开启通知栏消息提醒, MessageAlter已经被去掉，这里需要直接设置为开启
        val config = StatusBarNotificationConfig().apply {
            notificationExtraType = NotificationExtraTypeEnum.MESSAGE
            notificationSmallIconId = R.mipmap.book_archive
            notificationEntrance = MainActivity::class.java
            notificationFoldStyle = NotificationFoldStyle.CONTACT
            ring = false
            vibrate = false
            downTimeToggle = false
        }
        NIMClient.updateStatusBarNotificationConfig(config)
        NIMClient.toggleNotification(true)
        setPushAlter(true)
        updateMyIMUserInfo()
    }

    /**
     * 获取会话总未读数
     */
    val totalUnreadCount: Int
        get() {
            val unreadCount = v2NIMConversationService.totalUnreadCount
            log("会话消息总未读数 $unreadCount")
            return unreadCount
        }


    /**
     * 标记会话已读
     * 并触发 onTotalUnreadCountChanged、onConversationChanged 和 onUnreadCountChangedByFilter 回调，同步数据库和缓存。
     */
    fun clearUnreadCountByIds(conversationId: String) {
        v2NIMConversationService.clearUnreadCountByIds(listOf(conversationId), {
            log("标记会话已读成功 ${Gson().toJson(it)}")
        }) {
            log("标记会话已读失败 onFailed errorCode:${it.code}  desc:$${it.desc}")
        }
    }

    /**
     * 未读数清零
     */
    fun clearTotalUnreadCount() {
        v2NIMConversationService.clearTotalUnreadCount({
            log("未读数清零成功")
        }) {
            log("未读数清零失败 onFailed errorCode:${it.code}  desc:$${it.desc}")
        }
    }

    private fun updateMyIMUserInfo() {
//        val user = getUserInfo(account)
//        try {
//            user?.extensionMap?.let { extension ->
//
//                val fields = kotlin.collections.HashMap<UserInfoFieldEnum, Any>().apply {
//                    put(UserInfoFieldEnum.EXTEND, Gson().toJson(extension))
//                }
//                NIMClient.getService(UserService::class.java).updateUserInfo(fields)
//                    .setCallback(object : RequestCallbackWrapper<Void>() {
//                        override fun onResult(code: Int, result: Void?, exception: Throwable?) {
//                            Timber.d("updateUserInfo code: $code}")
//                            Timber.d("updateUserInfo res: ${user.extension}")
//                        }
//                    })
//            }
//        } catch (e: Exception) {
//            Timber.d("NIMClient-updateUserInfo-error : ${e.localizedMessage}")
//        }
    }

    private val _messageStatus = MutableLiveData<IMMessage>()
    val messageStatus: LiveData<IMMessage> = _messageStatus

    private val messageStatusObserver: Observer<IMMessage> = Observer<IMMessage> {
        Timber.d("observeMsgStatus:${Gson().toJson(it)}")
        if (it.status == MsgStatusEnum.success) {
            // 1、根据sessionId判断是否是自己的消息
            // 2、更改内存中消息的状态
            // 3、刷新界面
            _messageStatus.value = it
        }
    }


    fun v2SendMessage(v2Message: V2NIMMessage, accountId: String?, errorCallback: ((Int) -> Unit)? = null) {
        val conversationId =
            V2NIMConversationIdUtil.conversationId(accountId, V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P)

        val messageConfig = V2NIMMessageConfig.V2NIMMessageConfigBuilder
            .builder()
            .build()
        val sendMessageParams = V2NIMSendMessageParams.V2NIMSendMessageParamsBuilder
            .builder()
            .withMessageConfig(messageConfig)
            .build()
        v2MessageService.sendMessage(v2Message, conversationId, sendMessageParams, { result ->
            val message = result.message
            log("发送消息成功: $message")
        }, { failure ->
            log("发送消息, code: " + failure.code + ", message: " + failure.desc)
            when (failure.code) {
                SEND_IM_INSUFFICIENT_BALANCE -> {
                    CommonApplicationProxy.application.toast(R.string.insufficient_balance)
                    WalletManager.refreshWallet()
                }
            }
            errorCallback?.invoke(failure.code)
        }) { progress ->
            log("发送消息进度: $progress")
        }
    }

    /**
     * 打开、关闭推送服务
     */
    fun setPushAlter(isOpen: Boolean, callback: RequestCallback<Void>? = null) {

    }

    fun setChattingAccount(account: String) {
        // 进入聊天界面，建议放在onResume中。表示来自account的消息无需进行消息提醒。
        NIMClient.getService(MsgService::class.java).setChattingAccount(account, SessionTypeEnum.P2P)
    }

    fun setChattingAccountAll() {
        // 进入最近联系人列表界面，建议放在onResume中。表示所有消息无需进行消息提醒。
        NIMClient.getService(MsgService::class.java)
            .setChattingAccount(MsgService.MSG_CHATTING_ACCOUNT_ALL, SessionTypeEnum.None)
    }

    fun setChattingAccountNone() {
        // 退出聊天界面或离开最近联系人列表界面，建议放在onPause中。表示所有消息都可以进行消息提醒。
        NIMClient.getService(MsgService::class.java)
            .setChattingAccount(MsgService.MSG_CHATTING_ACCOUNT_NONE, SessionTypeEnum.None)
    }

}
