package com.tencent.qcloud.tuikit.tuicallkit

import android.content.Context
import android.content.Intent
import com.adealink.frame.aab.util.getCompatString
import com.adealink.frame.base.AppBase
import com.adealink.frame.log.Log
import com.adealink.frame.router.Router
import com.adealink.frame.util.AppUtil
import com.adealink.frame.util.runOnUiThread
import com.adealink.weparty.call.R
import com.adealink.weparty.call.constant.TAG_CALL_FLOW
import com.adealink.weparty.call.constant.TAG_CALL_KIT
import com.adealink.weparty.call.stat.CallEndEvent
import com.adealink.weparty.call.util.PermissionRequest
import com.adealink.weparty.call.util.ScreenWakeupHelper
import com.adealink.weparty.commonui.toast.util.showToast
import com.adealink.weparty.module.call.Call
import com.adealink.weparty.module.im.IM
import com.adealink.weparty.module.room.RoomModule
import com.tencent.cloud.tuikit.engine.call.TUICallDefine
import com.tencent.cloud.tuikit.engine.call.TUICallEngine
import com.tencent.cloud.tuikit.engine.call.TUICallObserver
import com.tencent.cloud.tuikit.engine.common.TUICommonDefine
import com.tencent.cloud.tuikit.engine.common.TUICommonDefine.Callback
import com.tencent.qcloud.tuicore.TUIConstants
import com.tencent.qcloud.tuicore.TUICore
import com.tencent.qcloud.tuicore.TUILogin
import com.tencent.qcloud.tuicore.permission.PermissionCallback
import com.tencent.qcloud.tuicore.permission.PermissionRequester
import com.tencent.qcloud.tuikit.tuicallkit.common.data.Constants
import com.tencent.qcloud.tuikit.tuicallkit.common.metrics.KeyMetrics
import com.tencent.qcloud.tuikit.tuicallkit.common.utils.CallUtils
import com.tencent.qcloud.tuikit.tuicallkit.common.utils.DeviceUtils
import com.tencent.qcloud.tuikit.tuicallkit.manager.CallManager
import com.tencent.qcloud.tuikit.tuicallkit.manager.PushManager
import com.tencent.qcloud.tuikit.tuicallkit.manager.UserManager
import com.tencent.qcloud.tuikit.tuicallkit.state.GlobalState
import com.tencent.qcloud.tuikit.tuicallkit.state.ViewState.ViewRouter
import com.tencent.qcloud.tuikit.tuicallkit.view.CallAdapter
import com.tencent.qcloud.tuikit.tuicallkit.view.component.incomingbanner.IncomingNotificationBanner
import io.trtc.tuikit.atomicxcore.api.CompletionHandler
import io.trtc.tuikit.atomicxcore.api.call.CallMediaType
import io.trtc.tuikit.atomicxcore.api.call.CallParams
import io.trtc.tuikit.atomicxcore.api.call.CallParticipantInfo
import io.trtc.tuikit.atomicxcore.api.call.CallParticipantStatus
import io.trtc.tuikit.atomicxcore.api.call.CallStore
import io.trtc.tuikit.atomicxcore.api.device.DeviceError
import io.trtc.tuikit.atomicxcore.api.device.DeviceStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope

class TUICallKitImpl private constructor(context: Context) : TUICallKit() {
    private val context = context.applicationContext ?: context

    private var screenWakeupHelper: ScreenWakeupHelper? = null
    private var subscribeSelfStateJob: Job? = null
    private val callObserver: TUICallObserver = object : TUICallObserver() {
        override fun onCallReceived(
            callId: String?,
            callerId: String?,
            calleeIdList: List<String?>?,
            mediaType: TUICallDefine.MediaType?,
            info: TUICallDefine.CallObserverExtraInfo?
        ) {
            //通话开始
            if (!TUILogin.isUserLogined()) {
                return
            }
            Log.i(TAG_CALL_FLOW, "onCallReceived($mediaType), callId:$callId")

            if (RoomModule.isInRoom()) {
                //用户在房,在线忙
                Log.i(TAG_CALL_FLOW, "onCallReceived($mediaType), user is room, ignore ~")
                TUICallEngine.createInstance(context).ignore(object : TUICommonDefine.Callback {
                    override fun onSuccess() {
                        Log.i(TAG_CALL_FLOW, "onCallReceived($mediaType), user is room, ignore success")
                    }

                    override fun onError(errCode: Int, errMsg: String?) {
                        Log.i(TAG_CALL_FLOW, "onCallReceived($mediaType), user is room, ignore fail, $errCode, $errMsg")
                    }
                })
                return
            }

            callId?.let {
                KeyMetrics.countUV(KeyMetrics.EventId.RECEIVED, callId)
            }
            CallManager.instance.selectAudioPlaybackDevice(TUICommonDefine.AudioPlaybackDevice.Speakerphone)
        }

        override fun onUserLineBusy(userId: String?) {
            super.onUserLineBusy(userId)
            if (CallUtils.getCallee() == userId) {
                //呼叫在线忙
                showToast(R.string.callkit_toast_other_party_busy)
            }
        }

        override fun onCallBegin(
            callId: String?,
            mediaType: TUICallDefine.MediaType?,
            info: TUICallDefine.CallObserverExtraInfo?
        ) {
            if (!TUILogin.isUserLogined()) {
                return
            }
            val isAppInBackground = !DeviceUtils.isAppRunningForeground(context)
            Log.i(
                TAG_CALL_FLOW,
                "onCallBegin($mediaType), callId:$callId, isAppInBackground:$isAppInBackground"
            )
            if (isAppInBackground) {
                showToast(
                    getCompatString(R.string.call_begin_in_background, AppBase.appName)
                )
            } else {
                CallManager.instance.startForegroundService()
            }
            DeviceStore.shared().openLocalMicrophone(null)
        }

        override fun onCallEnd(
            callId: String?,
            mediaType: TUICallDefine.MediaType?,
            reason: TUICallDefine.CallEndReason?,
            userId: String?,
            totalTime: Long,
            info: TUICallDefine.CallObserverExtraInfo?
        ) {
            if (!TUILogin.isUserLogined()) {
                return
            }
            Log.i(TAG_CALL_FLOW, "onCallEnded($mediaType), callId:$callId, reason:$reason")
            var toastText: String? = null

            val activeCall = CallStore.shared.observerState.activeCall.value
            val selfInfo = CallStore.shared.observerState.selfInfo.value
            if (activeCall.inviteeIds.size > 1 || activeCall.chatGroupId.isNotEmpty() || selfInfo.id == userId) {
                return
            }

            when (reason) {
                TUICallDefine.CallEndReason.HANGUP -> toastText =
                    context.getString(R.string.callkit_toast_other_party_hung_up)

                TUICallDefine.CallEndReason.REJECT -> toastText =
                    context.getString(R.string.callkit_toast_other_party_declined)

                TUICallDefine.CallEndReason.NO_RESPONSE -> toastText =
                    context.getString(R.string.callkit_toast_other_party_no_response)

                TUICallDefine.CallEndReason.LINE_BUSY -> toastText =
                    context.getString(R.string.callkit_toast_other_party_busy)

                TUICallDefine.CallEndReason.CANCELED -> toastText =
                    context.getString(R.string.callkit_toast_other_party_cancelled)

                else -> {}
            }

            if (!toastText.isNullOrEmpty()) {
                showToast(toastText)
            }
            CallEndEvent().apply {
                duration to CallUtils.getCallDuration()
            }.send()
        }
    }

    init {
        registerObserver()
    }

    override fun setSelfInfo(nickname: String?, avatar: String?, completion: CompletionHandler?) {
        CallManager.instance.setSelfInfo(nickname, avatar, completion)
    }

    override fun calls(
        userIdList: List<String>,
        mediaType: CallMediaType,
        params: CallParams?,
        completion: CompletionHandler?
    ) {
        val list = userIdList.toHashSet().toMutableList()
        if (list.isEmpty()) {
            Log.e(TAG_CALL_KIT, "calls failed, userIdList is empty")
            completion?.onFailure(
                TUICallDefine.ERROR_PARAM_INVALID,
                "calls failed, userIdList is empty"
            )
            return
        }
        PermissionRequest.requestPermissions(context, mediaType, object : PermissionCallback() {
            override fun onGranted() {
                CallManager.instance.calls(list, mediaType, params, object : CompletionHandler {
                    override fun onSuccess() {
                        startCallActivity()
                        completion?.onSuccess()
                    }

                    override fun onFailure(code: Int, desc: String) {
                        completion?.onFailure(code, desc)
                        CallManager.instance.reset()
                    }
                })
            }

            override fun onDenied() {
                Log.w(TAG_CALL_KIT, "calls, request Permissions failed")
                completion?.onFailure(
                    TUICallDefine.ERROR_PERMISSION_DENIED,
                    "request Permissions failed"
                )
            }
        })
    }

    override fun join(callId: String?, completion: CompletionHandler?) {
        PermissionRequest.requestPermissions(
            context,
            CallMediaType.Audio,
            object : PermissionCallback() {
                override fun onGranted() {
                    CallManager.instance.join(callId, object : Callback {
                        override fun onSuccess() {
                            startCallActivity()
                            completion?.onSuccess()
                        }

                        override fun onError(errCode: Int, errMsg: String?) {
                            completion?.onFailure(errCode, errMsg ?: "")
                            CallManager.instance.reset()
                        }
                    })
                }

                override fun onDenied() {
                    Log.w(TAG_CALL_KIT, "join, request Permissions failed")
                    completion?.onFailure(
                        TUICallDefine.ERROR_PERMISSION_DENIED,
                        "request Permissions failed"
                    )
                }
            })
    }

    override fun callExperimentalAPI(jsonStr: String) {
        CallManager.instance.callExperimentalAPI(jsonStr)
    }

    override fun setCallingBell(filePath: String?) {
        CallManager.instance.setCallingBell(filePath)
    }

    override fun enableMuteMode(enable: Boolean) {
        CallManager.instance.enableMuteMode(enable)
    }

    override fun enableFloatWindow(enable: Boolean) {
        CallManager.instance.enableFloatWindow(enable)
    }

    override fun enableVirtualBackground(enable: Boolean) {
        CallManager.instance.enableVirtualBackground(enable)
    }

//    override fun enableIncomingBanner(enable: Boolean) {
//        CallManager.instance.enableIncomingBanner(enable)
//    }

    override fun setScreenOrientation(orientation: Int) {
        CallManager.instance.setScreenOrientation(orientation)
    }

    override fun disableControlButton(button: Constants.ControlButton?) {
        CallManager.instance.disableControlButton(button)
    }

    override fun setAdapter(adapter: CallAdapter?) {
        Log.i(TAG_CALL_KIT, "setAdapter, adapter: $adapter")
        GlobalState.instance.callAdapter = adapter
    }

    fun queryOfflineCall() {
        if (RoomModule.isInRoom()) {
            //用户在房,在线忙
            Log.w(TAG_CALL_KIT, "handleNewCall, return, for in room")
            CallStore.shared.reject(null)
            return
        }

        Log.i(TAG_CALL_KIT, "queryOfflineCall start")
        val selfUser = CallStore.shared.observerState.selfInfo.value
        val mediaType = CallStore.shared.observerState.activeCall.value.mediaType
        if (CallParticipantStatus.Accept == selfUser.status) {
            return
        }

        if (null == mediaType) {
            Log.w(TAG_CALL_KIT, "queryOfflineCall, current status is Unknown")
            return
        }

        //The received call has been processed in #onCallReceived
        if (!selfIsCaller() && PermissionRequester.newInstance(PermissionRequester.BG_START_PERMISSION)
                .has()
        ) {
            return
        }
        val callId = CallStore.shared.observerState.activeCall.value.callId
        if (checkAlreadyCalled(callId)) {
            Log.d(TAG_CALL_FLOW, "queryOfflineCall return, for callId:${callId} already received.")
            return
        }
        handleNewCallWhenAppInForeground()
    }

    private val historyCallIds = mutableSetOf<String>()
    private fun checkAlreadyCalled(callId: String?): Boolean {
        callId ?: return false
        //用于查询去重
        if (historyCallIds.contains(callId)) {
            return true
        }
        historyCallIds.add(callId)
        return false
    }

    private fun isIMActivity(): Boolean {
        val activity = AppUtil.currentActivity ?: return false
        val iMPageName = Router.getClazz(IM.Session.PATH)?.name ?: return false
        return activity.javaClass.name == iMPageName
    }

    /**
     * 应用前台
     */
    private fun handleNewCallWhenAppInForeground() {
        startFullScreenView()
//        val isIMPage = isIMActivity()
//        when {
//            isIMPage -> {
//                startFullScreenView()
//            }
//
////            hasFloatPermission -> {
////                startSmallIncomingView(
////                    InComingFloatView(InComingFloatData(IBaseFloatData.MODE_SYSTEM))
////                )
////            }
//
//            else -> {
//                startSmallScreenView(
//                    startSmallScreenView(IncomingNotificationBanner(context))
//                )
//            }
//        }
    }


    private fun registerObserver() {
        TUICallEngine.createInstance(context).addObserver(callObserver)
        subscribeSelfStateJob = CoroutineScope(Dispatchers.Main).launch {
            supervisorScope {
                launch { observeSelfInfo() }
                launch { observeCameraStatus() }
            }
        }
    }

    private suspend fun observeSelfInfo() {
        CallStore.shared.observerState.selfInfo.collect { selfInfo ->
            Log.i(TAG_CALL_KIT, "selfInfo id=${selfInfo.id} status=${selfInfo.status}")
            notifyInternalEvent()
            if (selfInfo.status == CallParticipantStatus.None || RoomModule.isInRoom()) {
                CallManager.instance.reset()
                CallManager.instance.dismissCallingFloatWindow()
                return@collect
            }
            if (selfInfo.status != CallParticipantStatus.Waiting || selfIsCaller()) {
                return@collect
            }
            handleNewCall()
        }
    }

    private suspend fun observeCameraStatus() {
        DeviceStore.shared().deviceState.cameraLastError.collect { error ->
            if (error == DeviceError.OCCUPIED_ERROR) {
                DeviceStore.shared().closeLocalCamera()
            }
        }
    }

    private fun handleNewCall() {
        if (RoomModule.isInRoom()) {
            //用户在房,在线忙
            Log.w(TAG_CALL_KIT, "handleNewCall, return, for in room")
            CallStore.shared.reject(null)
            return
        }

        val floatPermission =
            PermissionRequester.newInstance(PermissionRequester.FLOAT_PERMISSION).has()
        val isAppInBackground = !DeviceUtils.isAppRunningForeground(context)
        val bgPermission =
            PermissionRequester.newInstance(PermissionRequester.BG_START_PERMISSION).has()
        val notificationPermission = PermissionRequest.isNotificationEnabled()

        val pushData = PushManager.getPushData()
        val isFCMDataChannel = pushData.channelId == TUIConstants.DeviceInfo.BRAND_GOOGLE_ELSE
        screenWakeupHelper?.releaseLock()

        Log.i(
            TAG_CALL_KIT,
            "handleNewCall, isAppInBackground: $isAppInBackground, floatPermission: $floatPermission" +
                    ", backgroundStartPermission: $bgPermission, notificationPermission: $notificationPermission , " +
                    "pushData: $pushData"
        )

        if (DeviceUtils.isScreenLocked(context)) {
            handleScreenLocked(isAppInBackground, isFCMDataChannel, notificationPermission)
            return
        }

        if (isAppInBackground) {
            handleAppInBackground(
                floatPermission,
                isFCMDataChannel,
                notificationPermission,
                bgPermission
            )
            return
        }

        handleNewCallWhenAppInForeground()
    }

    private fun handleScreenLocked(
        appInBackground: Boolean,
        fcmData: Boolean,
        notificationPermission: Boolean
    ) {
        Log.i(TAG_CALL_KIT, "handleScreenLocked, screen is locked")
        if (appInBackground && fcmData && notificationPermission) {
            startSmallScreenView(IncomingNotificationBanner(context))
        } else {
            startFullScreenView()
        }
    }

    private fun handleAppInBackground(
        floatPermission: Boolean, fcmData: Boolean,
        notificationPermission: Boolean, bgPermission: Boolean
    ) {
        when {
            //floatPermission -> startSmallScreenView(IncomingFloatBanner(context))
            fcmData && notificationPermission -> startSmallScreenView(
                IncomingNotificationBanner(
                    context
                )
            )

            bgPermission -> startFullScreenView()
            else -> {
                Log.w(TAG_CALL_KIT, "App is in background with no permission")
                showToast(
                    getCompatString(R.string.call_begin_in_background, AppBase.appName)
                )
            }
        }
    }

    private fun startFullScreenView() {
        Log.i(TAG_CALL_KIT, "startFullScreenView")
        if (CallStore.shared.observerState.selfInfo.value.status == CallParticipantStatus.None) {
            Log.i(TAG_CALL_KIT, "startFullScreenView, current status: None, ignore")
            return
        }
        val mediaType = CallStore.shared.observerState.activeCall.value.mediaType
        if (mediaType == null) {
            Log.e(TAG_CALL_KIT, "startFullScreenView fail, unknown mediaType")
            return
        }
        PermissionRequest.requestPermissions(context, mediaType, object
            : PermissionCallback() {
            override fun onGranted() {
                if (CallParticipantStatus.None != CallStore.shared.observerState.selfInfo.value.status) {
                    Log.i(TAG_CALL_KIT, "startFullScreenView requestPermissions onGranted")
                    startCallActivity()
                } else {
                    CallManager.instance.reset()
                }
            }

            override fun onDenied() {
                if (!selfIsCaller()) {
                    CallStore.shared.reject(null)
                }
                CallManager.instance.reset()
            }
        })
    }

    private fun startSmallScreenView(view: Any) {
        if (CallManager.instance.viewState.router.get() == ViewRouter.FullView) {
            return
        }
        val inviterId = CallStore.shared.observerState.activeCall.value.inviterId
        val caller = CallParticipantInfo()
        caller.id = inviterId
        val callStatus = CallStore.shared.observerState.selfInfo.value.status

        val list = ArrayList<String?>()
        list.add(caller.id)
        UserManager.instance.updateUserListInfo(
            list,
            object : TUICommonDefine.ValueCallback<List<CallParticipantInfo>?> {
                override fun onSuccess(data: List<CallParticipantInfo>?) {
                    if (callStatus == CallParticipantStatus.None) {
                        Log.w(TAG_CALL_KIT, "startSmallScreenView, current status: None, ignore")
                        return
                    }
                    caller.avatarUrl = data?.firstOrNull()?.avatarUrl ?: ""
                    caller.name = data?.firstOrNull()?.name ?: ""
                    if (view is IncomingNotificationBanner) {
                        view.showNotification(caller)
                    }
                }

                override fun onError(errCode: Int, errMsg: String?) {
                    if (view is IncomingNotificationBanner) {
                        view.showNotification(caller)
                    }
                }
            })
    }

    private fun startCallActivity() {
        wakeupScreen()
        Router.build(context, Call.Call.PATH)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            .start()
    }

    private fun notifyInternalEvent() {
        val selfUser = CallStore.shared.observerState.selfInfo.value
        if (selfUser.status == CallParticipantStatus.None) {
            TUICore.notifyEvent(
                TUIConstants.Privacy.EVENT_ROOM_STATE_CHANGED,
                TUIConstants.Privacy.EVENT_SUB_KEY_ROOM_STATE_STOP,
                null
            )
            if (selfIsCaller()) {
                TUICore.notifyEvent(EVENT_KEY_TIME_LIMIT, EVENT_SUB_KEY_COUNTDOWN_END, null)
            }
            return
        }
        if (selfUser.status == CallParticipantStatus.Accept) {
            if (selfIsCaller()) {
                TUICore.notifyEvent(EVENT_KEY_TIME_LIMIT, EVENT_SUB_KEY_COUNTDOWN_START, null)
            }
            if (TUICore.getService(TUIConstants.Service.TUI_PRIVACY) != null) {
                val map = HashMap<String, Any?>()
                map[TUIConstants.Privacy.PARAM_DIALOG_CONTEXT] = context
                TUICore.callService(
                    TUIConstants.Service.TUI_PRIVACY,
                    TUIConstants.Privacy.METHOD_ANTO_FRAUD_REMINDER,
                    map,
                    null
                )
            }
        }
    }

    private fun selfIsCaller(): Boolean {
        val selfId = CallStore.shared.observerState.selfInfo.value.id
        val callerId = CallStore.shared.observerState.activeCall.value.inviterId
        return selfId == callerId
    }

    private fun wakeupScreen() {
        runOnUiThread {
            ScreenWakeupHelper(context).wakeupIfPowerOff().also {
                screenWakeupHelper = it
            }
        }
    }

    companion object {
        private const val EVENT_KEY_TIME_LIMIT = "RTCRoomTimeLimitService"
        private const val EVENT_SUB_KEY_COUNTDOWN_START = "CountdownStart"
        private const val EVENT_SUB_KEY_COUNTDOWN_END = "CountdownEnd"
        private var instance: TUICallKitImpl? = null
        fun createInstance(context: Context): TUICallKitImpl {
            if (instance == null) {
                synchronized(TUICallKitImpl::class.java) {
                    if (instance == null) {
                        instance = TUICallKitImpl(context)
                    }
                }
            }
            return instance!!
        }
    }
}