package com.ydl.audioim

import android.annotation.SuppressLint
import android.content.Context
import android.os.Handler
import android.text.TextUtils
import com.alibaba.android.arouter.launcher.ARouter
import com.google.gson.Gson
import com.ydl.audioim.bean.AgoraInvitationBean
import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.command.ConnectExceptionCommand
import com.ydl.audioim.router.AudioImIn
import com.ydl.audioim.utils.AudioLogUtils
import com.ydl.consultantim.ConsultantAudioHomeActivity
import com.ydl.ydl_av.messge_service.YDLRTMClient
import com.ydl.ydl_av.messge_service.bean.RTMMesssage
import com.ydl.ydl_av.messge_service.callback.CallListener
import com.ydl.ydl_av.messge_service.callback.CancelCallStatusListener
import com.ydl.ydl_av.messge_service.callback.InitListener
import com.ydl.ydl_av.messge_service.callback.LoginCallback
import com.ydl.ydl_av.messge_service.request.LoginParam
import com.ydl.ydl_av.messge_service.response.CallLocalResponse
import com.ydl.ydl_av.messge_service.response.CallRemoteResponse
import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.utils.ActivityManager
import com.ydl.ydlcommon.utils.LogUtil
import com.ydl.ydlcommon.utils.log.LogHelper
import com.yidianling.common.tools.ToastUtil
import com.yidianling.im.api.bean.IMRegisterObserverCustomNotificationCallBack
import com.yidianling.im.api.bean.IMSendCustomNotificationResultCallBack
import com.yidianling.user.api.event.UserLoginEvent
import com.yidianling.user.api.event.UserLogoutEvent
import com.yidianling.user.api.service.IUserService
import de.greenrobot.event.EventBus
import io.agora.rtm.RtmStatusCode
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit

/**
 * @author harvie
 * @date 2019/9/27
 * 语音通话入口类
 */
class YDLavManager {

    companion object {
        const val FILE_NAME = "consult"
        //当前sdk的登录状态
        var sdkStatus = -1

        val instances: YDLavManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            YDLavManager()
        }
    }

    private constructor()

    fun init(context: Context, appId: String) {
        YDLRTMClient.instances.init(context, appId, listener)
        EventBus.getDefault().register(this)
        //设置回调
        setCallback()

        val uid = ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid
        if (!TextUtils.isEmpty(uid) && !TextUtils.equals("0", uid)) {
            //不延时,可能会导致请求api报 network not unablibale
            Handler().postDelayed({
                login(uid)
            }, 300)
        }
    }

    public fun onEvent(event: UserLoginEvent) {
        instances.login(event.uid)
    }

    public fun onEvent(event: UserLogoutEvent) {
        instances.logout()
    }

    private fun setCallback() {
        YDLRTMClient.instances.setCallListener(object : CallListener {
            override fun onCallRecivedByPeer(response: CallLocalResponse?) {
                //返回给主叫:被叫已收到呼叫邀请
                LogUtil.e("[agora]${response?.calleeId}已收到呼叫邀请,频道号${response?.ChannelId}")

                val act = ActivityManager.getInstance().getTopTaskActivity()
                if (act is AudioHomeActivity) {
                    act.runOnUiThread {
                        act.playWaitingMusic()
                    }
                }
            }

            override fun onCallAccepted(response: CallLocalResponse?, msg: String?) {
                //返回给主叫
                LogUtil.e("[agora]${response?.calleeId}已接收呼叫邀请")

                //加入声网频道时机修改:主叫收到被叫接受邀请的回调后再加入声网频道
                val act = ActivityManager.getInstance().getTopTaskActivity()
                if (act is AudioHomeActivity) {
                    act.runOnUiThread {
                        act.joinChannel()
                    }
                }
            }

            override fun onCallRefused(response: CallLocalResponse?, msg: String?) {
                //返回给主叫
                LogUtil.e("[agora]${response?.calleeId}已拒绝呼叫邀请")

                val act = ActivityManager.getInstance().getTopTaskActivity()
                if (act is AudioHomeActivity) {
                    act.runOnUiThread {
                        callEndStatusUpdate(response?.ChannelId!!, 2, "被叫拒绝")
                        ToastUtil.toastShort("对方已挂断")
                        act.writeAgoraLog("被叫拒绝了通话邀请")
                        //通话结束或挂断时,上传日志文件
                        act.uploadLog()
                        act.leaveChannel()
                        act.uploadExceptionStatus("对方已拒绝", 2)
                    }
                }
            }

            override fun onCallCanceled(response: CallLocalResponse?) {
                //返回给主叫
                LogUtil.e("[agora]主叫已取消呼叫邀请")
                val act = ActivityManager.getInstance().getTopTaskActivity()
                if (act is AudioHomeActivity) {
                    act.runOnUiThread {
                        act.writeAgoraLog("主叫呼叫取消:超时或主动取消")
                        act.uploadExceptionStatus("已取消", 1)
                    }
                }
            }

            override fun onCallFailure(response: CallLocalResponse?, errorCode: Int) {
                //返回给主叫
                LogUtil.e("[agora]呼叫${response?.calleeId}用户失败:${response?.response}")
                val act = ActivityManager.getInstance().getTopTaskActivity()
                //专家离线或者30 秒后仍未收到专家响应,重新再邀请一次
                when (errorCode) {
                    //被叫不在线   呼叫邀请发出 30 秒后被叫仍未 ACK 响应呼叫邀请
                    RtmStatusCode.LocalInvitationError.LOCAL_INVITATION_ERR_PEER_OFFLINE, RtmStatusCode.LocalInvitationError.LOCAL_INVITATION_ERR_PEER_NO_RESPONSE -> {
                        if (act is AudioHomeActivity) {
                            act.runOnUiThread {
                                act.rtcCall()
                            }
                        }
                    }
                    RtmStatusCode.LocalInvitationError.LOCAL_INVITATION_ERR_INVITATION_EXPIRE -> { //呼叫邀请过期。被叫 ACK 响应呼叫邀请后 60 秒呼叫邀请未被取消、接受、拒绝,则呼叫邀请过期。
                        if (act is AudioHomeActivity) {
                            act.runOnUiThread {
                                sendCustomNotification(
                                    response?.calleeId!!,
                                    response?.ChannelId!!,
                                    "5"
                                )
                                callEndStatusUpdate(response.ChannelId!!, 2, "被叫超时未接听")
                                act.writeAgoraLog("呼叫失败:${errorCode}")
                                act.uploadExceptionStatus("对方未接听", 3)
//                                //通话结束或挂断时,上传日志文件
//                                act.uploadLog()
//                                act.leaveChannel()
                            }
                        }
                    }
                }
            }

            override fun onRemoteInvitationReceived(response: CallRemoteResponse?) {
                //返回给被叫
                LogUtil.e("[agora]收到来自${response?.callerId}的呼叫邀请")
                receivedCall(response?.content, "来自RTM")
            }

            override fun onRemoteInvitationAccepted(response: CallRemoteResponse?) {
                //返回给被叫
                LogUtil.e("[agora]接受来自${response?.callerId}的呼叫成功")
            }

            override fun onRemoteInvitationRefused(response: CallRemoteResponse?) {
                //返回给被叫
                LogUtil.e("[agora]已拒绝来自${response?.callerId}的呼叫")
                val act = ActivityManager.getInstance().getTopTaskActivity()
                if (act is ConsultantAudioHomeActivity) {
                    act.uploadExceptionStatus("已拒绝", 2)
                }
            }

            override fun onRemoteInvitationCanceled(response: CallRemoteResponse?) {
                callEndStatusUpdate(response?.ChannelId!!, 1, "主叫取消呼叫")
                //返回给被叫
                LogUtil.e("[agora]主叫${response?.callerId}已取消呼叫邀请")
                AudioLogUtils.writeAgoraLog("呼叫邀请被取消:主叫主动取消", FILE_NAME)

                val act = ActivityManager.getInstance().getTopTaskActivity()
                if (act is ConsultantAudioHomeActivity) {
                    act.uploadExceptionStatus("对方已取消", 1)
                }

                closePage()
            }

            override fun onRemoteInvitationFailure(response: CallRemoteResponse?, errorCode: Int) {
                //返回给被叫
                LogUtil.e("[agora]来自主叫${response?.callerId}的呼叫邀请进程失败:${response?.response}")
                if (errorCode == RtmStatusCode.RemoteInvitationError.REMOTE_INVITATION_ERR_INVITATION_EXPIRE) { //呼叫邀请过期
                    AudioLogUtils.writeAgoraLog("呼叫邀请被取消:呼叫邀请过期", FILE_NAME)
                    val act = ActivityManager.getInstance().getTopTaskActivity()
                    if (act is ConsultantAudioHomeActivity) {
                        act.uploadExceptionStatus("未接听", 3)
                    }
                } else {
                    AudioLogUtils.writeAgoraLog("呼叫邀请被取消:错误原因(${errorCode})", FILE_NAME)
                }
                callEndStatusUpdate(response?.ChannelId!!, 2, "超时未接听导致的取消呼叫")
                //关闭页面
                closePage()
            }

            override fun onOtherMsg(error: String?) {
                LogUtil.e("[agora]其它消息:${error}")
            }
        })

        AudioImIn.registerObserveCustomNotification(object :
            IMRegisterObserverCustomNotificationCallBack {
            override fun onObserverCustomNotification(
                fromUid: String,
                toUid: String,
                content: String
            ) {
                LogUtil.e("[agora]收到云信的通知消息:$content")
                val agoraInvitationBean = Gson().fromJson(content, AgoraInvitationBean::class.java)

                //1发起呼叫 2接受呼叫  3取消呼叫  4拒绝呼叫邀请 5呼叫超时
                when (agoraInvitationBean.callType) {
                    "1" -> {
                        receivedCall(agoraInvitationBean.data, "来自云信")
                    }
                    "2" -> {
                        val act = ActivityManager.getInstance().getTopTaskActivity()
                        if (act is AudioHomeActivity) {
                            act.runOnUiThread {
                                act.joinChannel()
                            }
                        }
                    }
                    "3" -> {
                        AudioLogUtils.writeAgoraLog("呼叫邀请被取消:主叫主动取消", FILE_NAME)
                        closePage()
                    }
                    "4" -> {
                        val act = ActivityManager.getInstance().getTopTaskActivity()
                        if (act is AudioHomeActivity) {
                            act.runOnUiThread {
                                ToastUtil.toastShort("对方已挂断")
                                act.writeAgoraLog("被叫拒绝了通话邀请")
                                //通话结束或挂断时,上传日志文件
                                act.uploadLog()
                                act.leaveChannel()
                            }
                        }
                    }
                    "5" -> {
                        AudioLogUtils.writeAgoraLog("呼叫邀请被取消:呼叫邀请过期", FILE_NAME)
                        //关闭页面
                        closePage()
                    }
                }
            }
        })
    }

    fun rtcCall(listenerUid: String?, channelId: String?, sendDoctocrMsg: String?) {
        YDLRTMClient.instances.call(listenerUid, channelId, sendDoctocrMsg)

        sendCustomNotification(listenerUid!!, sendDoctocrMsg!!, "1")
    }

    fun acceptCall(toUid: String, channelId: String?, data: String) {
        YDLRTMClient.instances.acceptCall(channelId)
        sendCustomNotification(toUid, data, "2")
    }

    fun refuseCall(toUid: String, channelId: String?, data: String) {
        YDLRTMClient.instances.refuseCall(channelId)
        sendCustomNotification(toUid, data, "4")
    }

    fun cancelCall(
        listenerUid: String,
        channelId: String,
        data: String,
        event: (msg: String?, code: Int) -> Unit
    ) {
        YDLRTMClient.instances.cancelCall(
            listenerUid,
            channelId,
            object : CancelCallStatusListener {
                override fun onFailure(errorMsg: String?, errorCode: Int) {
                    event(errorMsg, errorCode)
                    YDLRTMClient.instances.cancelCall(listenerUid, channelId, null)
                }

                override fun onSuccess() {
                }
            })

        callEndStatusUpdate(channelId, 1, "主叫取消呼叫")
        sendCustomNotification(listenerUid, data, "3")
    }

    private fun sendCustomNotification(toUid: String, data: String, callType: String) {
        val infoBean = AgoraInvitationBean()
        infoBean.data = data
        infoBean.callType = callType

        Handler().postDelayed({
            AudioImIn.sendCustomNotification(toUid, Gson().toJson(infoBean),
                object : IMSendCustomNotificationResultCallBack {
                    override fun onException(throwable: Throwable) {
                    }

                    override fun onFailed(code: Int) {
                    }

                    override fun onSuccess() {
                    }
                })
        }, 300)

    }

    fun login(userId: String?) {
        login(userId) { _, _ ->
        }
    }

    @SuppressLint("CheckResult")
    fun login(userId: String?, event: (isSuccess: Boolean, msg: String?) -> Unit) {
        if (TextUtils.isEmpty(userId) || userId ?: "0" <= "0") {
            //如果uid为空或小于等于0 ,则不进行登录,因为uid为0也会登录成功,会导致后面uid正确时无法登录
            LogUtil.e("[agora]login-uid:$userId")
            return
        }
        //登录实时消息
        //获取token
        AudioApiRequestUtil.getAgoraToken().subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()).subscribe({
                if ("200".equals(it.code)) {
                    LogUtil.e("[agora]登录av的login-uid:$userId")
                    YDLRTMClient.instances.login(
                        LoginParam(userId, it.data.token),
                        object : LoginCallback {
                            override fun onSuccess() {
                                //登陆成功,发起呼叫
                                LogUtil.e("[agora]实时消息登录成功")
                                event(true, "")
                            }

                            override fun onFailure(msg: String?) {
                                LogUtil.e("[agora]实时消息登录失败:$msg")
                                event(false, msg)
                            }
                        })
                } else {
                    LogUtil.e("声网token获取失败uid:" + userId + " error:" + it.msg)
                    LogHelper.getInstance()
                        .writeLogSync("声网token获取失败uid:" + userId + " error:" + it.msg)
                }
            }, {
                LogUtil.e("声网token获取异常uid:" + userId + " error:" + it.message)
            })
    }

    /**
     * 收到邀请
     */
    @SuppressLint("CheckResult")
    fun receivedCall(content: String?, from: String = "") {
        if (!TextUtils.isEmpty(content)) {
            //如果已经接听了用户电话 再有电话进来 是不能接听的
            if (!activityIsExists(ConsultantAudioHomeActivity::class.java) && !activityIsExists(
                    AudioHomeActivity::class.java
                )
            ) {
                //延时启动通话界面,防止刚打开就被main遮挡
                Observable.timer(1000, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread()).subscribe {
                        LogUtil.e("[agora]启动通话界面")
                        //邀请加入频道消息,跳转通话界面
                        ARouter.getInstance().build("/av/ConsultantAudioHomeActivity")
                            .withString("param", content).navigation()

                        AudioLogUtils.writeAgoraLog("收到主叫方通话邀请($from)", FILE_NAME, false)
                    }
            } else {
                LogUtil.d("[agora]收到声网邀请,但界面实例已存在")
            }
        } else {
            LogUtil.d("[agora]收到声网邀请,但response==null")
        }
    }

    private fun activityIsExists(cls: Class<*>): Boolean {
        for (activity in ActivityManager.getInstance().getActivitys()) {
            if (activity.javaClass == cls) {
                return true
            }
        }
        return false
    }

    /**
     * 关闭通话界面
     */
    fun closePage() {
        var act = ActivityManager.getInstance().getTopTaskActivity()
        if (act is ConsultantAudioHomeActivity) {
            //未接通时,收到呼叫进程失败关闭页面,已接通无需关闭
            if (act.status == ConsultantAudioHomeActivity.STATUS_NOT_ANSWERED) {
                act.close(ConsultantAudioHomeActivity.RESULT_USER_CANCEL, "")
            }
        }
    }

    /**
     * 退出登录
     */
    fun logout() {
        EventBus.getDefault().unregister(this)
        YDLRTMClient.instances.logout(object : LoginCallback {
            override fun onSuccess() {
                //退出登陆成功
                LogUtil.d("[agora]实时消息退出成功")
            }

            override fun onFailure(msg: String?) {
                LogUtil.d("[agora]实时消息退出失败:$msg")
            }
        })
    }

    /**
     * RTM登录异常,上传错误日志 msg
     * 声网出现异常,上传错误日志 connectException
     */
    @SuppressLint("CheckResult")
    fun uploadException(
        connectException: ConnectExceptionCommand,
        callback: UploadExceptionCallback?
    ) {
        AudioApiRequestUtil.connectException(connectException).subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()).subscribe({
                callback?.onSuccess()
            }, {
                LogUtil.e("agora", "声网上传异常与错误日志接口调用失败:" + it.message)
            })
    }

    /**
     * 上传异常错误回调
     */
    interface UploadExceptionCallback {
        fun onSuccess()
    }


    @SuppressLint("CheckResult")
    fun callEndStatusUpdate(channelId: String, endStatus: Int, msg: String) {
        AudioApiRequestUtil.callEndStatusUpdate(channelId, endStatus, msg)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
            }, {
                LogUtil.d("callEndStatusUpdate error: ${it.message}")
            })
    }

    /**
     * 实时消息全局监听
     */
    private val listener = object : InitListener {

        override fun onTokenExpired() {
            LogUtil.e("[agora]onTokenExpired")
            instances.login(ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid)
        }

        override fun onMessageReceived(message: RTMMesssage, userId: Int) {
            LogUtil.i("[agora]onMessageReceived:${message.text} -->uid:$userId")
        }

        override fun onConnectionStateChanged(state: Int, reason: Int) {
            sdkStatus = state
            LogUtil.i("[agora]onConnectionStateChanged:state:${state} -->reason:$reason")
        }
    }
}