package com.adealink.weparty.room.sdk.controller.impl

import android.os.Handler
import android.os.SystemClock
import com.adealink.frame.aab.util.getCompatString
import com.adealink.frame.base.IError
import com.adealink.frame.base.Rlt
import com.adealink.frame.log.Log
import com.adealink.frame.media.data.RtcType
import com.adealink.frame.room.data.FlowStateInfo
import com.adealink.frame.room.data.JoinRoomReq
import com.adealink.frame.room.data.LeaveRoomReason
import com.adealink.frame.room.data.RoomSameJoiningError
import com.adealink.frame.room.data.RoomState
import com.adealink.frame.room.data.TAG_ROOM_FLOW
import com.adealink.frame.room.listener.IRoomStateChangeListener
import com.adealink.weparty.commonui.ext.isSuccess
import com.adealink.weparty.room.R
import com.adealink.weparty.room.constant.enterRoomJoinReqTime
import com.adealink.weparty.room.constant.enterRoomJoinResTime
import com.adealink.weparty.room.constant.logRoomTime
import com.adealink.weparty.room.data.getError
import com.adealink.weparty.room.datasource.local.RoomLocalService
import com.adealink.weparty.room.sdk.context.IRoomContext
import com.adealink.weparty.room.sdk.controller.BaseController
import com.adealink.weparty.room.sdk.controller.IJoinController
import com.adealink.weparty.room.sdk.listener.IJoinListener
import com.adealink.weparty.room.stat.RoomUnavailableEvent
import com.tencent.cloud.tuikit.engine.extension.TUILiveListManager
import com.tencent.cloud.tuikit.engine.room.TUIRoomDefine
import com.tencent.cloud.tuikit.engine.room.TUIRoomEngine
import com.tencent.qcloud.tuicore.TUIConfig
import com.trtc.tuikit.common.foregroundservice.AudioForegroundService
import io.trtc.tuikit.atomicxcore.api.call.CallStore
import io.trtc.tuikit.atomicxcore.api.live.LiveEndedReason
import io.trtc.tuikit.atomicxcore.api.live.LiveInfo
import io.trtc.tuikit.atomicxcore.api.live.LiveInfoCompletionHandler
import io.trtc.tuikit.atomicxcore.api.live.LiveKickedOutReason
import io.trtc.tuikit.atomicxcore.api.live.LiveListListener
import io.trtc.tuikit.atomicxcore.api.live.LiveListStore
import io.trtc.tuikit.atomicxcore.api.live.StopLiveCompletionHandler
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlin.coroutines.resume
import com.adealink.weparty.R as APP_R

open class JoinController(override val ctx: IRoomContext, serialHandler: Handler) :
    BaseController<IJoinListener>(ctx, serialHandler),
    IJoinController<IJoinListener>,
    IRoomStateChangeListener {
    private var currentRoomState = RoomState.ROOM_LEAVE
    private var currentFlowStateInfo = FlowStateInfo()

    override var joiningRoomId: String? = null
    override var joinedRoomInfo: LiveInfo? = null

    private var liveInfoObserve: Job? = null

    private val liveListListener = object : LiveListListener() {
        override fun onLiveEnded(liveID: String, reason: LiveEndedReason, message: String) {
            if (liveID != joinedRoomInfo?.liveID) return
            notifyLiveEnd(liveID, reason)
        }

        override fun onKickedOutOfLive(liveID: String, reason: LiveKickedOutReason, message: String) {
            super.onKickedOutOfLive(liveID, reason, message)
            if (liveID != joinedRoomInfo?.liveID) return
            notifyLiveKickedOut(liveID)
        }
    }

    override fun onRoomIn(flowStateInfo: FlowStateInfo) {
        super.onRoomIn(flowStateInfo)
        LiveListStore.shared().addLiveListListener(liveListListener)
        liveInfoObserve = launch {
            LiveListStore.shared().liveState.currentLive.collect {
                if (joinedRoomInfo?.liveID == it.liveID) {
                    joinedRoomInfo = it
                }
            }
        }
        startForegroundService()
    }

    override fun onRoomLeaved(flowStateInfo: FlowStateInfo) {
        super.onRoomLeaved(flowStateInfo)
        LiveListStore.shared().removeLiveListListener(liveListListener)
        liveInfoObserve?.cancel()
        stopForegroundService()
    }

    override fun onRoomStateChanged(
        fromState: RoomState,
        toState: RoomState,
        flowStateInfo: FlowStateInfo
    ) {
        super<BaseController>.onRoomStateChanged(fromState, toState, flowStateInfo)
        listeners.dispatch {
            it.onRoomStateChanged(fromState, toState, flowStateInfo)
        }
    }

    override fun notifyRoomStateChanged(
        fromState: RoomState,
        toState: RoomState,
        flowStateInfo: FlowStateInfo
    ) {
        Log.i(
            TAG_ROOM_FLOW,
            "notifyRoomStateChanged, fromState:$fromState, toState:$toState, flowStateInfo:$flowStateInfo"
        )
        ctx.roomService.controllers.forEach {
            it.onRoomStateChanged(fromState, toState, flowStateInfo)
        }
    }

    override fun getJoinedRoomId(): String? {
        return joinedRoomInfo?.liveID
    }

    override fun isOwner(): Boolean {
        return joinedRoomInfo?.liveOwner?.userID == ctx.appSupplier.selfUid
    }

    override fun isRoomJoiningOrJoined(): Boolean {
        return joiningRoomId != null || joinedRoomInfo != null
    }

    override suspend fun suspendJoinRoom(req: JoinRoomReq): Rlt<Any> {
        Log.d(TAG_ROOM_FLOW, "joinRoom: $req")
        return withContext(this.coroutineContext) {
            logRoomTime("joinRoom enter")
            val joinedRoomId = getJoinedRoomId()
            if (joinedRoomId == req.roomId) {
                Log.i(TAG_ROOM_FLOW, "joinRoom, already joined, joinedRoomId:${joinedRoomId}")
                return@withContext Rlt.Success(Any())
            }

            //先尝试退上次未退出的房间，然后再进房，重进房不执行退房逻辑
            val lastRoomId = RoomLocalService.lastEnterRoomId
            if (lastRoomId.isNotEmpty() && lastRoomId != req.roomId) {
                leaveLastRoom(lastRoomId, reason = LeaveRoomReason.ENTER_OTHER_ROOM, false)
            }

            if (joiningRoomId == req.roomId) {
                Log.i(TAG_ROOM_FLOW, "joinRoom, same room joining, joiningRoomId:${req.roomId}")
                return@withContext Rlt.Failed(RoomSameJoiningError())
            }

            joiningRoomId = req.roomId
            val joinRoomAsync = async { joinRoomInner(req) }
            val joinRoomRes = joinRoomAsync.await()
            logRoomTime("joinRoom res")
            Log.i(
                TAG_ROOM_FLOW,
                "joinRoomRes isSuccess:${joinRoomRes.isSuccess}"
            )

            if (joinRoomRes is Rlt.Failed) {
                val roomId = joinRoomRes.error.data as? String
                if (joiningRoomId == roomId) {
                    joiningRoomId = null
                }
                return@withContext joinRoomRes
            }

            joiningRoomId = null
            joinedRoomInfo = (joinRoomRes as Rlt.Success).data
            Log.i(TAG_ROOM_FLOW, "joinRoom: req:${req},joinedRoomInfo:${joinedRoomInfo}")
            ctx.mediaService.currRtcType = RtcType.T_RTC
            changeRoomState(RoomState.ROOM_IN, FlowStateInfo().apply {
                roomId = req.roomId
            })

            //进房成功,拉取必要的进房配置
            ctx.roomService.attrController.getNecessaryRoomConfig(req.roomId)
            logRoomTime("joinRoom end")
            return@withContext joinRoomRes
        }
    }

    override fun getJoinRoomState(): RoomState {
        return currentRoomState
    }

    override fun getJoinFlowData(): FlowStateInfo {
        return currentFlowStateInfo
    }

    private suspend fun joinRoomInner(req: JoinRoomReq): Rlt<LiveInfo> {
        return suspendCancellableCoroutine { coroutine ->
            enterRoomJoinReqTime = SystemClock.elapsedRealtime()
            LiveListStore.shared().joinLive(req.roomId, object : LiveInfoCompletionHandler {
                override fun onFailure(code: Int, desc: String) {
                    enterRoomJoinResTime = SystemClock.elapsedRealtime()
                    RoomUnavailableEvent().apply {
                        this.roomId to req.roomId
                        this.errorCode to code.toString()
                        this.errorReason to desc
                    }.send()
                    if (coroutine.isActive) {
                        coroutine.resume(
                            Rlt.Failed(getError(code, desc).apply {
                                data = req.roomId
                            })
                        )
                    }
                }

                override fun onSuccess(liveInfo: LiveInfo) {
                    enterRoomJoinResTime = SystemClock.elapsedRealtime()
                    if (coroutine.isActive) {
                        coroutine.resume(
                            Rlt.Success(liveInfo)
                        )
                    }
                }
            })
        }
    }

    override suspend fun leaveRoom(
        reason: LeaveRoomReason,
        leaveUI: Boolean
    ) {
        val joinedRoomId = getJoinedRoomId() ?: return
        withContext(this.coroutineContext) {
            leaveLastRoom(joinedRoomId, reason, leaveUI)
        }
    }


    private suspend fun leaveLastRoom(roomId: String?, reason: LeaveRoomReason, leaveUI: Boolean) {
        val joinedRoomId = roomId ?: return
        val flowStateInfo = FlowStateInfo().apply {
            this.roomId = joinedRoomId
            this.reason = reason.reason
            this.leaveUI = leaveUI
        }
        clearJoinedRoomInfo()
        innerLeaveRoom(joinedRoomId)
        changeRoomState(RoomState.ROOM_LEAVE, flowStateInfo)
    }

    private fun clearJoinedRoomInfo() {
        joinedRoomInfo = null
    }

    /**
     *  // json的格式如下
     *     // { "api" : "exitRoom",
     *     //   "params": {
     *     //      "roomId": 'xxx',   // 房间id 信息
     *     //      "syncWaiting": true   // 是否等待退房网络请求返回
     *     //      "roomType": 1/2    // 直播房间为2
     *     //    }
     *     // }
     */
    private suspend fun innerLeaveRoom(roomId: String): Rlt<Any> {
        if (roomId.isNullOrEmpty()) {
            return Rlt.Success(Any())
        }
        return suspendCancellableCoroutine { coroutine ->
            val leaveRoomReq = "{\"api\":\"exitRoom\",\"params\":{\"roomId\":\"$roomId\",\"syncWaiting\":true,\"roomType\":2}}"
            TUIRoomEngine.sharedInstance().callExperimentalAPI(leaveRoomReq, object : TUIRoomDefine.ExperimentalAPIResponseCallback {
                override fun onResponse(jsonData: String?) {
                    if (coroutine.isActive) {
                        coroutine.resume(
                            Rlt.Success(Any())
                        )
                    }
                }
            })
//            LiveListStore.shared().leaveLive(object : CompletionHandler {
//                override fun onFailure(code: Int, desc: String) {
//                    if (coroutine.isActive) {
//                        coroutine.resume(
//                            Rlt.Failed(IError(serverCode = code, msg = desc))
//                        )
//                    }
//                }
//
//                override fun onSuccess() {
//                    if (coroutine.isActive) {
//                        coroutine.resume(
//                            Rlt.Success(Any())
//                        )
//                    }
//                }
//            })
        }
    }

    override suspend fun closeRoom(reason: LeaveRoomReason) {
        val joinedRoomId = getJoinedRoomId() ?: return
        withContext(this.coroutineContext) {
            endLastRoom(joinedRoomId, reason)
        }
    }

    private suspend fun endLastRoom(roomId: String?, reason: LeaveRoomReason) {
        val joinedRoomId = roomId ?: return
        val flowStateInfo = FlowStateInfo().apply {
            this.roomId = joinedRoomId
            this.reason = reason.reason
            this.leaveUI = true
        }
        clearJoinedRoomInfo()
        innerEndRoom(joinedRoomId)
        changeRoomState(RoomState.ROOM_LEAVE, flowStateInfo)
    }

    private suspend fun innerEndRoom(roomId: String): Rlt<Any> {
        return suspendCancellableCoroutine { coroutine ->
            LiveListStore.shared().endLive(object : StopLiveCompletionHandler {
                override fun onFailure(code: Int, desc: String) {
                    if (coroutine.isActive) {
                        coroutine.resume(
                            Rlt.Failed(IError(serverCode = code, msg = desc))
                        )
                    }
                }

                override fun onSuccess(statisticsData: TUILiveListManager.LiveStatisticsData) {
                    if (coroutine.isActive) {
                        coroutine.resume(
                            Rlt.Success(Any())
                        )
                    }
                }
            })
        }
    }

    private fun notifyLiveEnd(roomId: String, reason: LiveEndedReason) {
        Log.i(TAG_ROOM_FLOW, "notifyLiveEnd, roomId:$roomId, reason:${reason.name}")
        launch {
            leaveRoom(LeaveRoomReason.LIVE_END, true)
        }
    }

    private fun notifyLiveKickedOut(roomId: String) {
        launch {
            leaveRoom(LeaveRoomReason.KICK_OUT, true)
        }
    }


    private fun changeRoomState(toState: RoomState, flowStateInfo: FlowStateInfo) {
        Log.i(
            TAG_ROOM_FLOW,
            "changeRoomState, currState:${currentRoomState}, toState:${toState}, flowStateInfo:${flowStateInfo}"
        )
        val lastState = currentRoomState
        currentRoomState = toState
        currentFlowStateInfo = flowStateInfo
        notifyRoomStateChanged(lastState, toState, flowStateInfo)
    }

    override fun onClear() {
        super.onClear()
        launch {
            leaveRoom(LeaveRoomReason.LOGOUT)
        }
    }


    private var isAudioForegroundServiceStart = false
    fun startForegroundService() {
        if (isAudioForegroundServiceStart) {
            Log.i(
                TAG_ROOM_FLOW,
                "startForegroundService return, for AudioForegroundService is running."
            )
            return
        }
        try {
            AudioForegroundService.start(
                TUIConfig.getAppContext(),
                getCompatString(R.string.room_service_title),
                "",
                APP_R.drawable.notification_ic
            )
            isAudioForegroundServiceStart = true
        } catch (e: Exception) {
            Log.e(TAG_ROOM_FLOW, "startForegroundService fail, for ${e.message}", e)
        }
    }

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

    }

}
