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

import android.os.Handler
import com.adealink.frame.aab.util.getCompatString
import com.adealink.frame.base.IError
import com.adealink.frame.log.Log
import com.adealink.frame.media.listener.IMediaRtcListener
import com.adealink.frame.media.rtc.RtcRole
import com.adealink.frame.room.data.FlowStateInfo
import com.adealink.frame.room.data.TAG_ROOM_SEAT
import com.adealink.weparty.App
import com.adealink.weparty.hardware.Hardware
import com.adealink.weparty.hardware.HardwareClient
import com.adealink.weparty.hardware.HardwareClientId
import com.adealink.weparty.hardware.HardwarePriority
import com.adealink.weparty.module.profile.data.UserInfo
import com.adealink.weparty.module.room.data.isSeatEmpty
import com.adealink.weparty.room.applymic.ApplyMicStore
import com.adealink.weparty.room.constant.logRoomTime
import com.adealink.weparty.room.data.isAutoMuteMic
import com.adealink.weparty.room.sdk.context.IRoomContext
import com.adealink.weparty.room.sdk.controller.BaseController
import com.adealink.weparty.room.sdk.controller.ISeatController
import com.adealink.weparty.room.sdk.listener.IDeviceListener
import com.adealink.weparty.room.sdk.listener.ISeatListener
import com.adealink.weparty.room.sdk.service.roomService
import io.trtc.tuikit.atomicxcore.api.CompletionHandler
import io.trtc.tuikit.atomicxcore.api.live.DeviceControlPolicy
import io.trtc.tuikit.atomicxcore.api.live.LiveSeatStore
import io.trtc.tuikit.atomicxcore.api.live.SeatInfo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

class SeatController(override val ctx: IRoomContext, serialHandler: Handler) :
    BaseController<ISeatListener>(ctx, serialHandler),
    ISeatController<ISeatListener>,
    IDeviceListener,
    IMediaRtcListener {

    private val _selfMicSeatInfo = MutableStateFlow<SeatInfo?>(null)
    override var selfMicSeatInfo: StateFlow<SeatInfo?> = _selfMicSeatInfo.asStateFlow()

    private var liveSeatRoomId: String? = null
    private var liveSeatStore: LiveSeatStore? = null
    private var liveSeatJob: Job? = null

    private val _micSeatsFlow = MutableStateFlow<Map<Int, SeatInfo>>(emptyMap())
    override val micSeatsFlow: StateFlow<Map<Int, SeatInfo>> = _micSeatsFlow.asStateFlow()

    private val _speakingUsersFlow = MutableStateFlow<Map<String, Boolean>>(hashMapOf())
    override val speakingUsersFlow: StateFlow<Map<String, Boolean>> = _speakingUsersFlow.asStateFlow()

    override fun getMySeatInfo(): SeatInfo? {
        return _micSeatsFlow.value.values.find { it.userInfo.userID == ctx.appSupplier.selfUid }
    }

    override fun getMyMicIndex(): Int? {
        return _micSeatsFlow.value.values.find { it.userInfo.userID == ctx.appSupplier.selfUid }?.index
    }

    override fun onRoomIn(flowStateInfo: FlowStateInfo) {
        Log.d(TAG_ROOM_SEAT, "onRoomIn, ${flowStateInfo.roomId}")
        liveSeatRoomId = flowStateInfo.roomId
        liveSeatStore = LiveSeatStore.create(flowStateInfo.roomId).apply {
            liveSeatJob = observeSeatList(this)
        }
    }

    override fun onRoomLeaved(flowStateInfo: FlowStateInfo) {
        Log.d(
            TAG_ROOM_SEAT,
            "onRoomLeaved, ${flowStateInfo.roomId}, reason:${flowStateInfo.reason}"
        )
        liveSeatJob?.cancel()
        liveSeatStore = null
        _micSeatsFlow.update { hashMapOf() }
        _selfMicSeatInfo.update { null }
        _speakingUsersFlow.update { hashMapOf() }
        ApplyMicStore.removeRecord(liveSeatRoomId)
        liveSeatRoomId = null
    }

    private fun observeSeatList(seatStore: LiveSeatStore): Job {
        // 监听 seatList 变化，并更新您的麦位 UI
        return launch {
            seatStore.liveSeatState.seatList.onEach { seatInfoList ->
                val roomId = liveSeatRoomId ?: return@onEach
                Log.d(
                    TAG_ROOM_SEAT,
                    "Seat list updated(roomId:$roomId): ${
                        seatInfoList.joinToString(separator = ",") {
                            "(index:${it.index},uid:${it.userInfo.userID})"
                        }
                    }"
                )
                handleMicSeatsInfo(
                    roomId,
                    seatInfoList
                )
            }.launchIn(this)
            seatStore.liveSeatState.speakingUsers.onEach { speakingUsers ->
                val roomId = liveSeatRoomId ?: return@onEach
                //                    Log.d(
                //                        TAG_ROOM_SEAT,
                //                        "Speaking updated(roomId:$roomId): ${
                //                            speakingUsers.entries.joinToString(separator = ",") {
                //                                "${it.key}_${it.value}"
                //                            }
                //                        }"
                //                    )
                handleMicSeatSpeaking(speakingUsers)
            }.launchIn(this)
        }
    }

    override fun observeMicSeatsInfo(scope: CoroutineScope, action: (roomId: String, micSeatsInfo: List<SeatInfo>) -> Unit): Job {
        return scope.launch {
            liveSeatStore?.liveSeatState?.seatList?.collect { seatInfoList ->
                val roomId = liveSeatRoomId ?: return@collect
                action.invoke(roomId, seatInfoList)
            }
        }
    }

    private fun getJoinedRoomId(): String? = ctx.roomService.joinController.getJoinedRoomId()

    override fun handleMicSeatsInfo(
        roomId: String,
        micSeatsInfo: List<SeatInfo>,
        micUserInfo: Map<Long, UserInfo>?,
    ) {
        logRoomTime("SeatController, handleMicSeatsInfo")
        launch {
            logRoomTime("SeatController, handleMicSeatsInfo, enter")
            Log.d(
                TAG_ROOM_SEAT,
                "handleMicSeatsInfo, roomId:$roomId, micSeatsInfo:$micSeatsInfo, micUserInfo:$micUserInfo"
            )
            val joinedRoomId = getJoinedRoomId()
            if (roomId != joinedRoomId) {
                Log.e(
                    TAG_ROOM_SEAT,
                    "handleMicSeatsInfo, localRoomId:${joinedRoomId}, serverRoomId:${roomId}, room id not match"
                )
                return@launch
            }

//            handleRoomUserInfoMap(micUserInfo)
            logRoomTime("SeatController, handleRoomUserInfo")

            val selfUid = ctx.appSupplier.selfUid
            var newSelfMicSeatInfo: SeatInfo? = null
            val newUidToMicSeatMap = hashMapOf<String, SeatInfo>()
            val newMicSeats = hashMapOf<Int, SeatInfo>()
            val newUidMicSeats = hashMapOf<String, SeatInfo>()
            var micOnMemberCount = 0L
            micSeatsInfo.forEach {
                if (selfUid == it.userInfo.userID) {
                    newSelfMicSeatInfo = it
                }
                if (!it.isSeatEmpty()) {
                    micOnMemberCount++
                    newUidToMicSeatMap[it.userInfo.userID] = it
                }

                newMicSeats[it.index] = it

                if (!it.userInfo.userID.isNullOrEmpty()) {
                    newUidMicSeats[it.userInfo.userID] = it
                }
            }
            logRoomTime("SeatController, updateMicSeats")

            //自己上下麦处理
            val lastSelfMicSeatInfo = _selfMicSeatInfo.value
            if (_selfMicSeatInfo.value == null) {
                //麦下
                if (newSelfMicSeatInfo != null) {
                    //麦上
                    _selfMicSeatInfo.update { newSelfMicSeatInfo }
                    selfMicSeatOn(newSelfMicSeatInfo)
                }
            } else {
                //麦上
                if (newSelfMicSeatInfo == null) {
                    //麦下
                    _selfMicSeatInfo.update { null }
                    selfMicSeatOff()
                }
            }
            _selfMicSeatInfo.update { newSelfMicSeatInfo }
            _micSeatsFlow.update { newMicSeats }
            if (newSelfMicSeatInfo != null) {
                val remoteMute = !newSelfMicSeatInfo.userInfo.allowOpenMicrophone

                // 提取出“是否应该被静音”的核心逻辑
                val shouldMute = if (lastSelfMicSeatInfo == null || lastSelfMicSeatInfo.isSeatEmpty()) {
                    roomService.joinController.joinedRoomInfo?.isAutoMuteMic() ?: false
                } else {
                    roomService.deviceController.isLocalUserMute()
                }

                Log.d(TAG_ROOM_SEAT, "SeatController, remoteMute:$remoteMute, shouldMute:$shouldMute")

                // 逻辑合并，极为清晰
                if (remoteMute || shouldMute) {
                    roomService.deviceController.closeLocalMicrophone()
                } else {
                    roomService.deviceController.openLocalMicrophone()
                }
            }

            logRoomTime("SeatController, handleMicSeatsInfo, end")
        }
    }

    /**
     * 跟进新的光波状态，更新麦位光波
     */
    private fun handleMicSeatSpeaking(speakingMap: Map<String, Int>) {
        val newSpeakingUsers = mutableMapOf<String, Boolean>()
        speakingMap.forEach {
            val uid = it.key
            newSpeakingUsers[uid] = it.value > 0
        }
        _speakingUsersFlow.update { newSpeakingUsers }
    }

    override fun isOnMic(uid: String): Boolean {
        return _micSeatsFlow.value.values.any { it.userInfo.userID == uid }
    }

    override fun getMicSeatIndex(uid: String): Int? {
        return _micSeatsFlow.value.values.find { it.userInfo.userID == uid }?.index
    }

    override fun micOn(micInex: Int, onSuccess: (() -> Unit)?, onFail: ((error: IError) -> Unit)?) {
        liveSeatStore?.takeSeat(seatIndex = micInex, object : CompletionHandler {
            override fun onFailure(code: Int, desc: String) {
                onFail?.invoke(IError(serverCode = code, msg = desc))
            }

            override fun onSuccess() {
                onSuccess?.invoke()
            }

        })
    }

    override fun micOff(onSuccess: (() -> Unit)?, onFail: ((error: IError) -> Unit)?) {
        liveSeatStore?.leaveSeat(object : CompletionHandler {
            override fun onFailure(code: Int, desc: String) {
                onFail?.invoke(IError(serverCode = code, msg = desc))
            }

            override fun onSuccess() {
                onSuccess?.invoke()
            }

        })
    }

    override fun kick(uid: String, onSuccess: (() -> Unit)?, onFail: ((error: IError) -> Unit)?) {
        liveSeatStore?.kickUserOutOfSeat(uid, object : CompletionHandler {
            override fun onFailure(code: Int, desc: String) {
                onFail?.invoke(IError(serverCode = code, msg = desc))
            }

            override fun onSuccess() {
                onSuccess?.invoke()
            }

        })
    }

    override fun micLock(micInex: Int, onSuccess: (() -> Unit)?, onFail: ((error: IError) -> Unit)?) {
        liveSeatStore?.lockSeat(micInex, object : CompletionHandler {
            override fun onFailure(code: Int, desc: String) {
                onFail?.invoke(IError(serverCode = code, msg = desc))
            }

            override fun onSuccess() {
                onSuccess?.invoke()
            }

        })
    }

    override fun micUnLock(micInex: Int, onSuccess: (() -> Unit)?, onFail: ((error: IError) -> Unit)?) {
        liveSeatStore?.unlockSeat(micInex, object : CompletionHandler {
            override fun onFailure(code: Int, desc: String) {
                onFail?.invoke(IError(serverCode = code, msg = desc))
            }

            override fun onSuccess() {
                onSuccess?.invoke()
            }
        })
    }

    override fun micMute(uid: String, onSuccess: (() -> Unit)?, onFail: ((error: IError) -> Unit)?) {
        liveSeatStore?.closeRemoteMicrophone(uid, object : CompletionHandler {
            override fun onFailure(code: Int, desc: String) {
                onFail?.invoke(IError(serverCode = code, msg = desc))
            }

            override fun onSuccess() {
                onSuccess?.invoke()
            }
        })
    }

    override fun micUnMute(uid: String, onSuccess: (() -> Unit)?, onFail: ((error: IError) -> Unit)?) {
        liveSeatStore?.openRemoteMicrophone(
            uid,
            DeviceControlPolicy.UNLOCK_ONLY,
            object : CompletionHandler {
                override fun onFailure(code: Int, desc: String) {
                    onFail?.invoke(IError(serverCode = code, msg = desc))
                }

                override fun onSuccess() {
                    onSuccess?.invoke()
                }
            })
    }

    override suspend fun getMicSeatsInfo() {
//        val roomId = liveSeatRoomId ?: return
//        val seatInfoList = liveSeatStore?.liveSeatState?.seatList?.value ?: emptyList()
//        Log.d(
//            TAG_ROOM_SEAT,
//            "Seat list updated(roomId:$roomId): ${
//                seatInfoList.joinToString(separator = ",") {
//                    "(index:${it.index},uid:${it.userInfo.userID})"
//                }
//            }"
//        )
//        handleMicSeatsInfo(
//            roomId,
//            seatInfoList
//        )
    }

    /**
     * 自己上麦
     */
    private fun selfMicSeatOn(selfMicSeatInfo: SeatInfo) {
        Log.i(
            TAG_ROOM_SEAT,
            "selfMicSeatOn, roomId:${getJoinedRoomId()}, selfMicSeatInfo:$selfMicSeatInfo"
        )

        App.instance.hardwareManager.requestHardware(
            object : HardwareClient(
                HardwareClientId.ROOM.id,
                getCompatString(com.adealink.weparty.R.string.common_room)
            ) {
                override fun isActive(): Boolean {
                    val roomId = roomService.joinController.getJoinedRoomId()
                    if (roomId.isNullOrEmpty()) {
                        //不在房间
                        return false
                    }
                    return _selfMicSeatInfo.value != null
                }
            },
            HardwarePriority.HIGH,
            Hardware.MIC,
            null
        )
        liveSeatRoomId?.let { roomId ->
            ApplyMicStore.removeRecord(roomId)
        }
    }

    /**
     * 自己下麦
     */
    private fun selfMicSeatOff() {
        Log.i(TAG_ROOM_SEAT, "selfMicSeatOff, roomId:${getJoinedRoomId()}")
        ctx.mediaService.setClientRole(RtcRole.AUDIENCE)
        App.instance.hardwareManager.release(HardwareClientId.ROOM.id, Hardware.MIC)
        liveSeatRoomId?.let { roomId ->
            ApplyMicStore.removeRecord(roomId)
        }
    }

}