//
//  RoomEventDispatcher.swift
//  TUIRoomKit
//
//  Created by janejntang on 2023/8/14.
//

import Foundation
import RTCRoomEngine

class RoomEventDispatcher: NSObject {
    var engineManager: EngineManager {
        EngineManager.shared
    }
    var store: RoomStore {
        engineManager.store
    }
    var roomInfo: TUIRoomInfo {
        store.roomInfo
    }
    var currentUser: UserEntity {
        store.currentUser
    }
    
    deinit {
        debugPrint("deinit")
    }
}

extension RoomEventDispatcher: TUIRoomObserver {
    // MARK: - Room event callback
    func onAllUserMicrophoneDisableChanged(roomId: String, isDisable: Bool) {
        roomInfo.isMicrophoneDisableForAllUser = isDisable
        let param = [
            "roomId" : roomId,
            "isDisable" : isDisable,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onAllUserMicrophoneDisableChanged, param: param)
    }
    
    func onAllUserCameraDisableChanged(roomId: String, isDisable: Bool) {
        roomInfo.isCameraDisableForAllUser = isDisable
        let param = [
            "roomId" : roomId,
            "isDisable" : isDisable,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onAllUserCameraDisableChanged, param: param)
    }
    
    func onSendMessageForAllUserDisableChanged(roomId: String, isDisable: Bool) {
        roomInfo.isMessageDisableForAllUser = isDisable
    }
    
    func onRoomDismissed(roomId: String) {
        EngineEventCenter.shared.notifyEngineEvent(event: .onRoomDismissed, param: ["roomId" : roomId,])
    }
    
    func onKickedOutOfRoom(roomId: String, reason: TUIKickedOutOfRoomReason, message: String) {
        let param = [
            "roomId" : roomId,
            "reason" : reason,
            "message" : message,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onKickedOutOfRoom, param: param)
    }
    
    func onRoomNameChanged(roomId: String, roomName: String) {
        roomInfo.name = roomName
    }
    
    // MARK: - User event callback in the room
    func onRemoteUserEnterRoom(roomId: String, userInfo: TUIUserInfo) {
        store.remoteUserEnterRoom(userInfo: userInfo)
        let param = [
            "roomId" : roomId,
            "userInfo" : userInfo,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onRemoteUserEnterRoom, param: param)
    }
    
    func onRemoteUserLeaveRoom(roomId: String, userInfo: TUIUserInfo) {
        store.remoteUserLeaveRoom(userInfo: userInfo)
        let param = [
            "roomId" : roomId,
            "userInfo" : userInfo,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onRemoteUserLeaveRoom, param: param)
    }
    
    func onUserRoleChanged(userId: String, userRole: TUIRole) {
        userRoleChanged(userId: userId, userRole: userRole)
        let param = [
            "userId" : userId,
            "userRole" : userRole,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onUserRoleChanged, param: param)
    }
    
    func onUserVideoStateChanged(userId: String, streamType: TUIVideoStreamType, hasVideo: Bool, reason: TUIChangeReason) {
        userVideoStateChanged(userId: userId, streamType: streamType, hasVideo: hasVideo, reason: reason)
        let param = [
            "userId" : userId,
            "streamType" : streamType,
            "hasVideo" : hasVideo,
            "reason" : reason,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onUserVideoStateChanged, param: param)
    }
    
    func onUserAudioStateChanged(userId: String, hasAudio: Bool, reason: TUIChangeReason) {
        userAudioStateChanged(userId: userId, hasAudio: hasAudio, reason: reason)
        let param = [
            "userId" : userId,
            "hasAudio" : hasAudio,
            "reason" : reason,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onUserAudioStateChanged, param: param)
    }
    
    func onUserVoiceVolumeChanged(volumeMap: [String : NSNumber]) {
        userVoiceVolumeChanged(volumeMap: volumeMap)
        EngineEventCenter.shared.notifyEngineEvent(event: .onUserVoiceVolumeChanged, param: volumeMap)
    }
    
    func onUserScreenCaptureStopped(reason: Int) {
        userScreenCaptureStopped()
        EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_SomeoneSharing, param: ["userId":currentUser.userId, "hasVideo": false])
    }
    
    func onSeatListChanged(seatList: [TUISeatInfo], seated seatedList: [TUISeatInfo], left leftList: [TUISeatInfo]) {
        seatListChanged(seatList: seatList,seated: seatedList, left: leftList)
        let param = [
            "seatList": seatList,
            "seated": seatedList,
            "left": leftList,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onSeatListChanged, param: param)
    }
    
    func OnSendMessageForUserDisableChanged(roomId: String, userId: String, isDisable muted: Bool) {
        store.updateUserDisableSendingMessage(userId: userId, isDisable: muted)
        let param = [
            "roomId": roomId,
            "userId": userId,
            "muted": muted,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onSendMessageForUserDisableChanged, param: param)
    }
    
    // MARK: - Signaling request related callbacks
    func onRequestReceived(request: TUIRequest) {
        requestReceived(request: request)
        EngineEventCenter.shared.notifyEngineEvent(event: .onRequestReceived, param: ["request": request,])
    }
    
    func onRequestCancelled(requestId: String, userId: String) {
        store.deleteTakeSeatRequest(requestId: requestId)
    }
    
    func onRequestProcessed(requestId: String, userId: String) {
        store.deleteTakeSeatRequest(requestId: requestId)
    }
    
    func onKickedOffSeat(userId: String) {
        let param = [
            "userId": userId,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onKickedOffSeat, param: param)
    }
    
    func onKickedOffLine(message: String) {
        kickedOffLine()
        let param = [
            "message": message,
        ] as [String : Any]
        EngineEventCenter.shared.notifyEngineEvent(event: .onKickedOffLine, param: param)
    }
}

extension RoomEventDispatcher {
    private func seatListChanged(seatList: [TUISeatInfo], seated: [TUISeatInfo], left leftList: [TUISeatInfo]) {
        store.updateLeftSeatList(leftList: leftList)
        store.updateSeatedList(seatList: seated)
    }
    
    private func userAudioStateChanged(userId: String, hasAudio: Bool, reason: TUIChangeReason) {
        if userId == currentUser.userId {
            EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_CurrentUserHasAudioStream, param: ["hasAudio": hasAudio, "reason": reason])
            currentUser.hasAudioStream = hasAudio
        }
        guard let userModel = store.attendeeList.first(where: { $0.userId == userId }) else { return }
        userModel.hasAudioStream = hasAudio
        EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: [:])
    }
    
    private func userVideoStateChanged(userId: String, streamType: TUIVideoStreamType, hasVideo: Bool, reason: TUIChangeReason) {
        switch streamType {
        case .screenStream:
            if userId == currentUser.userId {
                currentUser.hasScreenStream = hasVideo
            }
            guard let userModel = store.attendeeList.first(where: { $0.userId == userId }) else { return }
            userModel.hasScreenStream = hasVideo
            EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_SomeoneSharing, param: ["userId": userId, "hasVideo": hasVideo])
        case .cameraStream:
            if userId == currentUser.userId {
                EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_CurrentUserHasVideoStream,
                                                       param: ["hasVideo": hasVideo, "reason": reason])
                currentUser.hasVideoStream = hasVideo
                store.videoSetting.isCameraOpened = hasVideo
            }
            guard let userModel = store.attendeeList.first(where: { $0.userId == userId }) else { return }
            userModel.hasVideoStream = hasVideo
            EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: [:])
        default: break
        }
    }
    
    private func userRoleChanged(userId: String, userRole: TUIRole) {
        let isSelfRoleChanged = userId == currentUser.userId
        let isRoomOwnerChanged = userRole == .roomOwner
        if let userInfo = store.attendeeList.first(where: { $0.userId == userId }) {
            userInfo.userRole = userRole
            EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: ["userRole": userRole])
        }
        if isSelfRoleChanged {
            store.currentUser.userRole = userRole
            EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_CurrentUserRoleChanged, param: ["userRole": userRole])
        }
        if isRoomOwnerChanged {
            EngineManager.shared.fetchRoomInfo(roomId: roomInfo.roomId) { _ in
                EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RoomOwnerChanged, param: ["owner": userId])
            }
        }
        if checkAutoTakeSeatForOwner(userId: userId, userRole: userRole) {
           let _ = engineManager.takeSeat()
        }
        if checkAutoSendingMessageForOwner(userId: userId, userRole: userRole) {
            engineManager.disableSendingMessageByAdmin(userId: userId, isDisable: false)
        }
        if isSelfRoleChanged, userRole != .generalUser {
            engineManager.updateSeatApplicationList()
        }
    }
    
    private func checkAutoTakeSeatForOwner(userId: String, userRole: TUIRole) -> Bool {
        let isSelfRoleChanged = userId == currentUser.userId
        let isRoomOwnerChanged = userRole == .roomOwner
        return isSelfRoleChanged && isRoomOwnerChanged && roomInfo.isSeatEnabled && !currentUser.isOnSeat
    }
    
    private func checkAutoSendingMessageForOwner(userId: String, userRole: TUIRole) -> Bool {
        let isSelfRoleChanged = userId == currentUser.userId
        let isRoomOwnerChanged = userRole == .roomOwner
        return isSelfRoleChanged && isRoomOwnerChanged && currentUser.disableSendingMessage
    }
    
    private func requestReceived(request: TUIRequest) {
        switch request.requestAction {
        case .takeSeat:
            store.addInviteSeatUser(request: request)
        default: break
        }
    }
    
    private func userVoiceVolumeChanged(volumeMap: [String : NSNumber]) {
        for (userId, volume) in volumeMap {
            guard let userModel = store.attendeeList.first(where: { $0.userId == userId}) else { continue }
            userModel.userVoiceVolume = volume.intValue
        }
    }
    
    private func userScreenCaptureStopped() {
        currentUser.hasScreenStream = false
        guard let userModel = store.attendeeList.first(where: { $0.userId == currentUser.userId }) else { return }
        userModel.hasScreenStream = false
    }
    
    private func kickedOffLine() {
        engineManager.destroyEngineManager()
    }
}
