package com.tencent.qcloud.tuikit.tuicallkit.manager

import android.content.Context
import com.adealink.frame.aab.util.getCompatString
import com.adealink.frame.log.Log
import com.adealink.weparty.call.R
import com.adealink.weparty.call.constant.TAG_CALL_ENGINE
import com.adealink.weparty.call.constant.TAG_CALL_FLOW
import com.adealink.weparty.call.constant.TAG_CALL_KIT
import com.adealink.weparty.call.datasource.local.CallLocalService
import com.adealink.weparty.call.service.CallAudioForegroundService
import com.adealink.weparty.call.util.PermissionRequest
import com.adealink.weparty.call.widget.float.calling.CallingFloatData
import com.adealink.weparty.call.widget.float.calling.CallingFloatView
import com.adealink.weparty.commonui.toast.util.showToast
import com.adealink.weparty.commonui.widget.floatview.WindowManagerProxy
import com.adealink.weparty.commonui.widget.floatview.data.FloatWindowType
import com.adealink.weparty.commonui.widget.floatview.data.MODE_APPLICATION
import com.adealink.weparty.commonui.widget.floatview.data.MODE_SYSTEM
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.common.TUICommonDefine
import com.tencent.imsdk.BaseConstants
import com.tencent.qcloud.tuicore.TUIConfig
import com.tencent.qcloud.tuicore.permission.PermissionCallback
import com.tencent.qcloud.tuicore.permission.PermissionRequester
import com.tencent.qcloud.tuicore.util.ErrorMessageConverter
import com.tencent.qcloud.tuicore.util.SPUtils
import com.tencent.qcloud.tuikit.tuicallkit.common.data.Constants
import com.tencent.qcloud.tuikit.tuicallkit.common.utils.CallUtils
import com.tencent.qcloud.tuikit.tuicallkit.manager.feature.CallingBellFeature
import com.tencent.qcloud.tuikit.tuicallkit.state.GlobalState
import com.tencent.qcloud.tuikit.tuicallkit.state.ViewState
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.CallParticipantStatus
import io.trtc.tuikit.atomicxcore.api.call.CallStore
import io.trtc.tuikit.atomicxcore.api.device.AudioRoute
import io.trtc.tuikit.atomicxcore.api.device.DeviceStore
import io.trtc.tuikit.atomicxcore.api.login.LoginStore
import org.json.JSONObject
import com.adealink.weparty.R as APP_R

class CallManager private constructor(context: Context) {
    private val context: Context = context.applicationContext
    val viewState: ViewState = ViewState()

    fun reset() {
        stopForegroundService()
        viewState.reset()
    }

    fun calls(
        userIdList: List<String>, mediaType: CallMediaType, params: CallParams?,
        completion: CompletionHandler?
    ) {
        Log.i(TAG, "calls, userIdList: $userIdList, mediaType: $mediaType, params: $params")
        if (userIdList.isEmpty()) {
            Log.e(TAG, "calls failed, userIdList is empty")
            completion?.onFailure(
                TUICallDefine.ERROR_PARAM_INVALID,
                "calls failed, userIdList is empty"
            )
            return
        }
        if (userIdList.size >= Constants.MAX_USER) {
            showToast(getCompatString(R.string.callkit_user_exceed_limit))
            Log.e(TAG, "calls failed, exceeding max user number: 9")
            completion?.onFailure(
                TUICallDefine.ERROR_PARAM_INVALID,
                "calls failed, exceeding max user number"
            )
            return
        }

        CallStore.shared.calls(userIdList, mediaType, params, object : CompletionHandler {
            override fun onSuccess() {
                Log.i(TAG, "calls success. list: $userIdList")
                completion?.onSuccess()
            }

            override fun onFailure(code: Int, desc: String) {
                Log.e(TAG, "calls failed, errCode: $code, errMsg: $desc")
                val errMessage: String = convertErrorMsg(code, desc)
                showToast(errMessage)
                completion?.onFailure(code, errMessage)
            }
        })
    }

    fun join(callId: String?, callback: TUICommonDefine.Callback?) {
        Log.i(TAG, "join, callId: $callId")
        if (callId.isNullOrEmpty()) {
            Log.e(TAG, "join failed, callId is empty")
            callback?.onError(TUICallDefine.ERROR_PARAM_INVALID, "join failed, callId is empty")
            return
        }

        CallStore.shared.join(callId, object : CompletionHandler {
            override fun onSuccess() {
                callback?.onSuccess()
            }

            override fun onFailure(code: Int, desc: String) {
                Log.e(TAG, "join failed callId: $callId, errCode: $code, errMsg: $desc")
                showToast(convertErrorMsg(code, desc))
                callback?.onError(code, desc)
            }
        })
    }

    fun inviteUser(userIdList: List<String?>?, completion: CompletionHandler?) {
        Log.i(TAG, "inviteUser, userIdList: $userIdList")
        val filteredList = userIdList?.filterNotNull()?.distinct() ?: emptyList()
        if (filteredList.isEmpty()) {
            Log.e(TAG, "inviteUser failed, userIdList is empty")
            return
        }
        val params = CallParams()
        CallStore.shared
            .invite(filteredList, params, object : CompletionHandler {

                override fun onSuccess() {
                    Log.i(TAG, "inviteUser success")
                    completion?.onSuccess()
                }

                override fun onFailure(code: Int, desc: String) {
                    Log.e(TAG, "inviteUser failed, errCode: $code, errMsg: $desc")
                    completion?.onFailure(code, desc)
                }
            })
    }

    fun reject(completion: CompletionHandler?) {
        Log.i(TAG, "reject")
        CallStore.shared.reject(object : CompletionHandler {
            override fun onSuccess() {
                completion?.onSuccess()
            }

            override fun onFailure(code: Int, desc: String) {
                Log.e(TAG, "reject failed, errorCode: $code, errMsg: $desc")
                completion?.onFailure(code, desc)
            }
        })
        reset()
    }

    fun hangup(completion: CompletionHandler?) {
        Log.i(TAG, "hangup")
        CallStore.shared.hangup(object : CompletionHandler {
            override fun onSuccess() {
                completion?.onSuccess()
            }

            override fun onFailure(code: Int, desc: String) {
                Log.e(TAG, "reject failed, errorCode: $code, errMsg: $desc")
                completion?.onFailure(code, desc)
            }
        })
        reset()
    }

    fun selectAudioPlaybackDevice(device: TUICommonDefine.AudioPlaybackDevice) {
        val currentAudioRoute =
            if (device == TUICommonDefine.AudioPlaybackDevice.Speakerphone) AudioRoute.SPEAKERPHONE else AudioRoute.EARPIECE
        DeviceStore.shared().setAudioRoute(currentAudioRoute)
    }

    fun enableMultiDeviceAbility(enable: Boolean, completion: CompletionHandler?) {
        Log.i(TAG, "enableMultiDeviceAbility, enable: $enable")
        TUICallEngine.createInstance(context)
            .enableMultiDeviceAbility(enable, object : TUICommonDefine.Callback {
                override fun onSuccess() {
                    completion?.onSuccess()
                }

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

            })
    }

    fun setSelfInfo(nickname: String?, avatar: String?, completion: CompletionHandler?) {
        Log.i(TAG, "setSelfInfo, nickname: $nickname, avatar: $avatar")
        TUICallEngine.createInstance(context)
            .setSelfInfo(nickname, avatar, object : TUICommonDefine.Callback {
                override fun onSuccess() {
                    completion?.onSuccess()
                }

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

    fun setCallingBell(filePath: String?) {
        Log.i(TAG, "setCallingBell, filePath: $filePath")
        SPUtils.getInstance(CallingBellFeature.PROFILE_TUICALLKIT)
            .put(CallingBellFeature.PROFILE_CALL_BELL, filePath)
    }

    fun enableMuteMode(enable: Boolean) {
        Log.i(TAG, "enableMuteMode, enable: $enable")
        GlobalState.instance.enableMuteMode = enable
        SPUtils.getInstance(CallingBellFeature.PROFILE_TUICALLKIT)
            .put(CallingBellFeature.PROFILE_MUTE_MODE, enable)
    }

    fun enableFloatWindow(enable: Boolean) {
        Log.i(TAG, "enableFloatWindow, enable: $enable")
        GlobalState.instance.enableFloatWindow = enable
    }

    fun enableVirtualBackground(enable: Boolean) {
        Log.i(TAG, "enableVirtualBackground, enable: $enable")
        GlobalState.instance.enableVirtualBackground = enable
        val data = HashMap<String, Any>()
        data[Constants.KEY_VIRTUAL_BACKGROUND]
        reportOnlineLog(data)
    }

//    fun enableIncomingBanner(enable: Boolean) {
//        Log.i(TAG, "enableIncomingBanner, enable: $enable")
//        GlobalState.instance.enableIncomingBanner = enable
//    }

    fun setScreenOrientation(orientation: Int) {
        Log.i(TAG, "setScreenOrientation, orientation: $orientation")
        if (orientation in 0..2) {
            GlobalState.instance.orientation = Constants.Orientation.values()[orientation]
        }

        if (orientation == Constants.Orientation.LandScape.ordinal) {
            val videoEncoderParams = TUICommonDefine.VideoEncoderParams()
            videoEncoderParams.resolutionMode =
                TUICommonDefine.VideoEncoderParams.ResolutionMode.Landscape
            TUICallEngine.createInstance(context).setVideoEncoderParams(videoEncoderParams, null)
        }
    }

    fun disableControlButton(button: Constants.ControlButton?) {
        Log.i(TAG, "disableControlButton, button: $button")
        button?.let { GlobalState.instance.disableControlButtonSet.add(it) }
    }

    fun callExperimentalAPI(jsonStr: String) {
        if (jsonStr.isNullOrEmpty()) {
            Log.w(TAG, "callExperimentalAPI, jsonStr is empty");
            return
        }
        try {
            val json = JSONObject(jsonStr)
            if (!json.has("api") || !json.has("params")) {
                Log.e(TAG, "callExperimentalAPI[lack api or illegal params]: $jsonStr")
                return
            }
            val api = json.getString("api")
            val params = json.getJSONObject("params")
            Log.i(TAG, "callExperimentalAPI, api: $api, params: $params")
            if (api == "forceUseV2API") {
                if (params.has("enable")) {
                    GlobalState.instance.enableForceUseV2API = params.getBoolean("enable")
                }
            }
            if (api == "setFramework") {
                if (params.has("framework")) {
                    Constants.framework = params.getInt("framework")
                }
                if (params.has("component")) {
                    Constants.component = params.getInt("component")
                }
                if (params.has("language")) {
                    Constants.language = params.getInt("language")
                }
                val params = JSONObject()
                params.put("framework", Constants.framework)
                params.put("component", Constants.component)
                params.put("language", Constants.language)

                val jsonObject = JSONObject()
                jsonObject.put("api", "setFramework")
                jsonObject.put("params", params)
                TUICallEngine.createInstance(context).callExperimentalAPI(jsonObject.toString())
            }
        } catch (e: Exception) {
            Log.e(TAG, "callExperimentalAPI json parse fail，json: $jsonStr, error: $e")
        }
    }

    private fun convertErrorMsg(errorCode: Int, errMsg: String): String {
        if (errorCode == BaseConstants.ERR_SVR_MSG_IN_PEER_BLACKLIST) {
            return context.getString(R.string.callkit_error_in_peer_blacklist)
        }

        val commonErrorMap = getCommonErrorMap()
        if (commonErrorMap.containsKey(errorCode)) {
            return commonErrorMap[errorCode]!!
        }
        return ErrorMessageConverter.convertIMError(errorCode, errMsg)
    }

    private fun getCommonErrorMap(): Map<Int, String> {
        val map = HashMap<Int, String>()
        map[TUICallDefine.ERROR_PACKAGE_NOT_PURCHASED] =
            context.getString(R.string.callkit_package_not_purchased)
        map[TUICallDefine.ERROR_PACKAGE_NOT_SUPPORTED] =
            context.getString(R.string.callkit_package_not_support)
        map[TUICallDefine.ERROR_INIT_FAIL] = context.getString(R.string.callkit_error_invalid_login)
        map[TUICallDefine.ERROR_PARAM_INVALID] =
            context.getString(R.string.callkit_error_parameter_invalid)
        map[TUICallDefine.ERROR_REQUEST_REFUSED] =
            context.getString(R.string.callkit_error_request_refused)
        map[TUICallDefine.ERROR_REQUEST_REPEATED] =
            context.getString(R.string.callkit_error_request_repeated)
        map[TUICallDefine.ERROR_SCENE_NOT_SUPPORTED] =
            context.getString(R.string.callkit_error_scene_not_support)
        return map
    }

    private var isAudioForegroundServiceStart = false
    fun startForegroundService() {
        if (isAudioForegroundServiceStart) {
            Log.i(
                TAG_CALL_FLOW,
                "startForegroundService return, for AudioForegroundService is running."
            )
            return
        }
        if (CallUtils.getSelfCallStatus() != CallParticipantStatus.Accept) {
            stopForegroundService()
            return
        }
        val mediaType = CallStore.shared.observerState.activeCall.value.mediaType
        if (mediaType == CallMediaType.Audio) {
            try {
                CallAudioForegroundService.start(
                    TUIConfig.getAppContext(),
                    getCompatString(R.string.call_calling_service_title),
                    "",
                    APP_R.drawable.notification_ic
                )
                isAudioForegroundServiceStart = true
            } catch (e: Exception) {
                Log.e(TAG_CALL_FLOW, "startForegroundService fail, for ${e.message}", e)
            }
        }
    }

    fun stopForegroundService() {
        try {
            CallAudioForegroundService.stop(TUIConfig.getAppContext())
            isAudioForegroundServiceStart = false
        } catch (e: Exception) {
            Log.e(TAG_CALL_FLOW, "stopForegroundService fail, for ${e.message}", e)
        }

    }

    private fun reportOnlineLog(data: Map<String, Any>) {
        try {
            val map: JSONObject = JSONObject(data)
            map.put("version", TUICallDefine.VERSION)
            map.put("platform", "android")
            map.put("framework", "native")
            map.put("sdk_app_id", LoginStore.shared.sdkAppID)

            val params = JSONObject()
            params.put("level", 1)
            params.put("msg", map.toString())
            params.put("more_msg", "TUICallKit")

            val jsonObject = JSONObject()
            jsonObject.put("api", "reportOnlineLog")
            jsonObject.put("params", params)

            TUICallEngine.createInstance(context).trtcCloudInstance.callExperimentalAPI(jsonObject.toString())
        } catch (e: Exception) {
            Log.e(TAG, "reportOnlineLog fail, error: $e")
        }
    }

    fun showCallingFloatWindow(finishActivity: (() -> Unit)? = null) {
        Log.d(TAG_CALL_ENGINE, "showCallingFloatWindow")
        if (RoomModule.isInRoom()) {
            //用户在房,在线忙
            Log.w(TAG_CALL_KIT, "showCallingFloatWindow return, for in room")
            dismissCallingFloatWindow()
            return
        }
        if (CallUtils.getSelfCallStatus() == CallParticipantStatus.None) {
            Log.d(TAG_CALL_ENGINE, "showCallingFloatWindow return, for callStatus is none")
            dismissCallingFloatWindow()
            return
        }

        val floatViewAdded =
            WindowManagerProxy.getWindowManager().isFloatViewAdded(FloatWindowType.CALL_1V1_CALLING)
        if (floatViewAdded) {
            Log.d(TAG_CALL_FLOW, "showCallingFloatWindow return, for callingFloatView is show")
            return
        }

        if (PermissionRequester.newInstance(
                PermissionRequester.FLOAT_PERMISSION,
                PermissionRequester.BG_START_PERMISSION
            ).has()
        ) {
            CallingFloatView(CallingFloatData(MODE_SYSTEM)).showCallingView()
            finishActivity?.invoke()
            return
        }
        if (!CallLocalService.requestOverPlayPermission) {
            CallingFloatView(CallingFloatData(MODE_APPLICATION)).showCallingView()
            finishActivity?.invoke()
            return
        }
        CallLocalService.requestOverPlayPermission = false
        PermissionRequest.requestFloatPermission(context, object : PermissionCallback() {
            override fun onGranted() {
                CallingFloatView(CallingFloatData(MODE_SYSTEM)).showCallingView()
                finishActivity?.invoke()
            }

            override fun onDenied() {
                super.onDenied()
                CallingFloatView(CallingFloatData(MODE_APPLICATION)).showCallingView()
                finishActivity?.invoke()
            }
        })
    }

    fun dismissCallingFloatWindow() {
        Log.d(TAG_CALL_ENGINE, "dismissCallingFloatWindow")
        CallingFloatView.dismiss()
    }

    companion object {
        private const val TAG = "CallManager"
        val instance: CallManager = CallManager(TUIConfig.getAppContext())
        private const val BLUR_LEVEL_HIGH = 3
        private const val BLUR_LEVEL_CLOSE = 0
    }
}