//
//  UserListManagerViewModel.swift
//  TUIRoomKit
//
//  Created by janejntang on 2023/2/10.
//  Copyright © 2023 Tencent. All rights reserved.
//

import Foundation
import RTCRoomEngine

protocol UserListManagerViewEventResponder: AnyObject {
    func makeToast(text: String)
    func showAlert(title: String?, message: String?, sureTitle:String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?)
    func setUserListManagerViewHidden(isHidden: Bool)
    func dismissView()
    func updateUI(item: ButtonItemData)
    func updateStackView(items:[ButtonItemData])
    func addStackView(item: ButtonItemData, index: Int?)
    func removeStackView(itemType: ButtonItemData.ButtonType)
    func showUserNameInputView(userName: String?)
}

class UserListManagerViewModel: NSObject {
    var selectUserId: String = ""
    let timeoutNumber: Double = 60
    var userListManagerItems: [ButtonItemData] = []
    weak var viewResponder: UserListManagerViewEventResponder?
    var engineManager: EngineManager {
        EngineManager.shared
    }
    var roomInfo: TUIRoomInfo {
        engineManager.store.roomInfo
    }
    var currentUser: UserEntity {
        engineManager.store.currentUser
    }
    var attendeeList: [UserEntity] {
        engineManager.store.attendeeList
    }
    var selectUserInfo: UserEntity? {
        attendeeList.first(where: { $0.userId == selectUserId } )
    }
    
    init(selectUserId: String) {
        self.selectUserId = selectUserId
        super.init()
        createManagerItems()
        subscribeEngine()
    }
    
    deinit {
        unsubscribeEngine()
        debugPrint("self:\(self)")
    }
    
    private func subscribeEngine() {
        EngineEventCenter.shared.subscribeEngine(event: .onUserVideoStateChanged, observer: self)
        EngineEventCenter.shared.subscribeEngine(event: .onUserAudioStateChanged, observer: self)
        EngineEventCenter.shared.subscribeEngine(event: .onSendMessageForUserDisableChanged, observer: self)
        EngineEventCenter.shared.subscribeEngine(event: .onSeatListChanged, observer: self)
        EngineEventCenter.shared.subscribeEngine(event: .onUserInfoChanged, observer: self)
    }
    
    private func unsubscribeEngine() {
        EngineEventCenter.shared.unsubscribeEngine(event: .onUserVideoStateChanged, observer: self)
        EngineEventCenter.shared.unsubscribeEngine(event: .onUserAudioStateChanged, observer: self)
        EngineEventCenter.shared.unsubscribeEngine(event: .onSendMessageForUserDisableChanged, observer: self)
        EngineEventCenter.shared.unsubscribeEngine(event: .onSeatListChanged, observer: self)
        EngineEventCenter.shared.unsubscribeEngine(event: .onUserInfoChanged, observer: self)
    }
    
    private func createManagerItems() {
        if checkMediaShownState() {
            userListManagerItems.append(muteAudioItem)
            userListManagerItems.append(muteVideoItem)
        }
        if checkInviteSeatItemShownState() {
            userListManagerItems.append(inviteSeatItem)
        }
        if checkChangeHostItemShownState() {
            userListManagerItems.append(changeHostItem)
        }
        if checkSetAdministratorItemShownState() {
            userListManagerItems.append(setAdministratorItem)
        }
        if checkModifyTheNameItemShownState() {
            userListManagerItems.append(modifyTheNameItem)
        }
        if checkMuteMessageItemShownState() {
            userListManagerItems.append(muteMessageItem)
        }
        if checkKickOutItemShownState() {
            userListManagerItems.append(kickOutItem)
        }
    }
    
    private lazy var muteAudioItem: ButtonItemData = {
        let item = ButtonItemData()
        item.normalTitle = .muteText
        item.normalIcon = "room_unMute_audio"
        item.selectedTitle = .requestOpenAudioText
        item.selectedIcon = "room_mute_audio"
        item.resourceBundle = tuiRoomKitBundle()
        item.buttonType = .muteAudioItemType
        item.isSelect = !(selectUserInfo?.hasAudioStream ?? true)
        item.hasLineView = true
        item.action = { [weak self] sender in
            guard let self = self, let button = sender as? UIButton else { return }
            self.muteAudioAction(sender: button)
        }
        return item
    }()
    
    private lazy var muteVideoItem: ButtonItemData = {
        let item = ButtonItemData()
        item.normalTitle = .closeVideoText
        item.normalIcon = "room_unMute_video"
        item.selectedTitle = .requestOpenVideoText
        item.selectedIcon = "room_mute_video"
        item.resourceBundle = tuiRoomKitBundle()
        item.buttonType = .muteVideoItemType
        item.isSelect = !(selectUserInfo?.hasVideoStream ?? true)
        item.hasLineView = true
        item.action = { [weak self] sender in
            guard let self = self, let button = sender as? UIButton else { return }
            self.muteVideoAction(sender: button)
        }
        return item
    }()
    
    private lazy var inviteSeatItem: ButtonItemData = {
        let item = ButtonItemData()
        item.normalTitle = .inviteSeatText
        item.normalIcon = "room_invite_seat"
        item.selectedTitle = .stepDownSeatText
        item.selectedIcon = "room_step_down_seat"
        item.resourceBundle = tuiRoomKitBundle()
        item.isSelect = selectUserInfo?.isOnSeat ?? false
        item.hasLineView = true
        item.action = { [weak self] sender in
            guard let self = self, let button = sender as? UIButton else { return }
            self.inviteSeatAction(sender: button)
        }
        return item
    }()
    
    private lazy var changeHostItem: ButtonItemData = {
        let item = ButtonItemData()
        item.normalTitle = .changeHostText
        item.normalIcon = "room_change_host"
        item.resourceBundle = tuiRoomKitBundle()
        item.hasLineView = true
        item.action = { [weak self] sender in
            guard let self = self, let button = sender as? UIButton else { return }
            self.changeHostAction(sender: button)
        }
        return item
    }()
    
    private lazy var muteMessageItem: ButtonItemData = {
        let item = ButtonItemData()
        item.normalTitle = .muteMessageText
        item.normalIcon = "room_mute_message"
        item.selectedTitle = .unMuteMessageText
        item.selectedIcon = "room_unMute_message"
        item.resourceBundle = tuiRoomKitBundle()
        item.isSelect = selectUserInfo?.disableSendingMessage ?? false
        item.hasLineView = true
        item.action = { [weak self] sender in
            guard let self = self, let button = sender as? UIButton else { return }
            self.muteMessageAction(sender: button)
        }
        return item
    }()
    
    private lazy var kickOutItem: ButtonItemData = {
        let item = ButtonItemData()
        item.normalTitle = .kickOutRoomText
        item.normalIcon = "room_kickOut_room"
        item.resourceBundle = tuiRoomKitBundle()
        item.hasLineView = true
        item.action = { [weak self] sender in
            guard let self = self, let button = sender as? UIButton else { return }
            self.kickOutAction(sender: button)
        }
        return item
    }()
    
    private lazy var setAdministratorItem: ButtonItemData = {
        let item = ButtonItemData()
        item.normalTitle = .setAsAdministratorText
        item.selectedTitle = .undoAdministratorText
        item.normalIcon = "room_set_administrator"
        item.selectedIcon = "room_undo_administrator"
        item.resourceBundle = tuiRoomKitBundle()
        item.isSelect = selectUserInfo?.userRole == .administrator
        item.hasLineView = true
        item.action = {  [weak self] sender in
            guard let self = self, let button = sender as? UIButton else { return }
            self.setAdministratorAction(sender: button)
        }
        return item
    }()
    
    private lazy var modifyTheNameItem: ButtonItemData = {
        let item = ButtonItemData()
        item.normalTitle = .modifyTheName
        item.normalIcon = "room_modify_name"
        item.resourceBundle = tuiRoomKitBundle()
        item.hasLineView = true
        item.action = { [weak self] sender in
            guard let self = self, let button = sender as? UIButton else { return }
            self.modifyTheNameAction(sender: button)
        }
        return item
    }()
    
    func backBlockAction(sender: UIView) {
        sender.isHidden = true
    }
    
    private func checkMediaShownState() -> Bool {
        guard selectUserId != currentUser.userId else { return false }
        if !roomInfo.isSeatEnabled {
            return true
        } else if let selectUserInfo = selectUserInfo, selectUserInfo.isOnSeat {
            return true
        } else {
            return false
        }
    }
    
    private func checkInviteSeatItemShownState() -> Bool {
        return roomInfo.isSeatEnabled && selectUserId != currentUser.userId
    }
    
    private func checkChangeHostItemShownState() -> Bool {
        return currentUser.userRole == .roomOwner && selectUserId != currentUser.userId
    }
    
    private func checkMuteMessageItemShownState() -> Bool {
        return selectUserId != currentUser.userId
    }
    
    private func checkKickOutItemShownState() -> Bool {
        return currentUser.userRole == .roomOwner && selectUserId != currentUser.userId
    }
    
    private func checkSetAdministratorItemShownState() -> Bool {
        return currentUser.userRole == .roomOwner && selectUserId != currentUser.userId
    }
    
    private func checkModifyTheNameItemShownState() -> Bool {
        return true
    }
}

extension UserListManagerViewModel {
    private func addViewItem(buttonItem: ButtonItemData, index: Int) {
        guard !isContainedViewItem(buttonType: buttonItem.buttonType) else { return }
        if userListManagerItems.count > index + 1 {
            userListManagerItems.insert(buttonItem, at: index)
            viewResponder?.addStackView(item: buttonItem, index: index)
        } else {
            userListManagerItems.append(buttonItem)
            viewResponder?.addStackView(item: buttonItem, index: nil)
        }
    }
    
    private func removeViewItem(buttonType: ButtonItemData.ButtonType) {
        userListManagerItems.removeAll(where: { $0.buttonType == buttonType })
        viewResponder?.removeStackView(itemType: buttonType)
    }
    
    private func isContainedViewItem(buttonType: ButtonItemData.ButtonType) -> Bool {
        return userListManagerItems.contains(where: { $0.buttonType == buttonType })
    }
    
    private func muteAudioAction(sender: UIButton) {
        guard let userInfo = attendeeList.first(where: { $0.userId == selectUserId }) else { return }
        if userInfo.hasAudioStream {
            engineManager.closeRemoteDeviceByAdmin(userId: selectUserId, device: .microphone)
        } else {
            engineManager.openRemoteDeviceByAdmin(userId: selectUserId, device: .microphone)
            viewResponder?.makeToast(text: .invitedOpenAudioText)
        }
        viewResponder?.dismissView()
    }
    
    private func muteVideoAction(sender: UIButton) {
        guard let userInfo = selectUserInfo else { return }
        if userInfo.hasVideoStream {
            engineManager.closeRemoteDeviceByAdmin(userId: selectUserId, device: .camera)
        } else {
            engineManager.openRemoteDeviceByAdmin(userId: selectUserId, device: .camera)
            viewResponder?.makeToast(text: .invitedOpenVideoText)
        }
        viewResponder?.dismissView()
    }
    
    func inviteSeatAction(sender: UIButton) {
        guard let userInfo = selectUserInfo else { return }
        sender.isSelected = !sender.isSelected
        if sender.isSelected {
            guard engineManager.store.seatList.count < roomInfo.maxSeatCount else {
                RoomRouter.makeToastInCenter(toast: .theStageIsFullText, duration: 0.5)
                viewResponder?.dismissView()
                return
            }
            engineManager.takeUserOnSeatByAdmin(userId: selectUserId, timeout: timeoutNumber) { _,_ in
                let text: String = localizedReplace(.onStageText, replace: userInfo.userName)
                RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
            } onRejected: { requestId, userId, message in
                let text: String = localizedReplace(.refusedTakeSeatInvitationText, replace: userInfo.userName)
                RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
            } onCancelled: { _, _ in
            } onTimeout: { _, _ in
                let text: String = localizedReplace(.takeSeatInvitationTimeoutText, replace: userInfo.userName)
                RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
            } onError: { _, _, code, message in
                let text: String = code == .requestIdRepeat ? .receivedSameRequestText : message
                RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
            }
            viewResponder?.makeToast(text: .invitedTakeSeatText)
        } else {
            engineManager.kickUserOffSeatByAdmin(userId: selectUserId)
        }
        viewResponder?.dismissView()
    }
    
    private func changeHostAction(sender: UIButton) {
        guard let userInfo = attendeeList.first(where: { $0.userId == selectUserId }) else { return }
        let title = localizedReplace(.transferHostTitle, replace: userInfo.userName)
        viewResponder?.showAlert(title: title, message: .transferHostMessage, sureTitle: .transferHostsureText, declineTitle: .cancelText, sureBlock: { [weak self] in
            guard let self = self else { return }
            if self.roomInfo.isScreenShareDisableForAllUser, self.currentUser.hasScreenStream {
                self.engineManager.stopScreenCapture()
            }
            self.engineManager.changeUserRole(userId: self.selectUserId, role: .roomOwner) { [weak self] in
                guard let self = self else { return }
                let text = localizedReplace(.haveTransferredMasterText, replace: userInfo.userName)
                self.viewResponder?.makeToast(text: text)
                self.viewResponder?.dismissView()
            } onError: { [weak self] code, message in
                guard let self = self else { return }
                self.viewResponder?.dismissView()
            }
        }, declineBlock: nil)
    }
    
    private func muteMessageAction(sender: UIButton) {
        guard let userInfo = attendeeList.first(where: { $0.userId == selectUserId }) else { return }
        engineManager.disableSendingMessageByAdmin(userId: selectUserId, isDisable: !userInfo.disableSendingMessage)
        viewResponder?.dismissView()
    }
    
    func kickOutAction(sender: UIButton) {
        let kickOutTitle = localizedReplace(.kickOutText, replace: selectUserInfo?.userName ?? "")
        viewResponder?.showAlert(title: kickOutTitle, message: nil, sureTitle: .alertOkText, declineTitle: .cancelText, sureBlock: { [weak self] in
            guard let self = self else { return }
            self.engineManager.kickRemoteUserOutOfRoom(userId: self.selectUserId)
            self.viewResponder?.dismissView()
        }, declineBlock: nil)
    }
    
    private func setAdministratorAction(sender: UIButton) {
        guard let userInfo = selectUserInfo else { return }
        let role: TUIRole = userInfo.userRole == .administrator ? .generalUser : .administrator
        engineManager.changeUserRole(userId: selectUserId, role: role) {  [weak self] in
            guard let self = self else { return }
            let setAdministratorText = localizedReplace(.setUpAdministratorText, replace: userInfo.userName)
            let removeAdministratorText = localizedReplace(.removedAdministratorText, replace: userInfo.userName)
            let text: String = role == .administrator ? setAdministratorText : removeAdministratorText
            self.viewResponder?.makeToast(text: text)
            if role == .generalUser, self.roomInfo.isScreenShareDisableForAllUser, userInfo.hasScreenStream {
                self.engineManager.closeRemoteDeviceByAdmin(userId: userInfo.userId, device: .screenSharing)
            }
            self.viewResponder?.dismissView()
        } onError: { [weak self] code, message in
            guard let self = self else { return }
            self.viewResponder?.dismissView()
            debugPrint("changeUserRole,code:\(code),message:\(message)")
        }
    }
    
    private func modifyTheNameAction(sender: UIButton) {
        viewResponder?.showUserNameInputView(userName: selectUserInfo?.userName)
    }
}

extension UserListManagerViewModel: RoomEngineEventResponder {
    func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
        switch name {
        case .onUserAudioStateChanged:
            guard let userId = param?["userId"] as? String else { return }
            guard let hasAudio = param?["hasAudio"] as? Bool else { return }
            guard userId == selectUserId else { return }
            muteAudioItem.isSelect = !hasAudio
            viewResponder?.updateUI(item: muteAudioItem)
        case .onUserVideoStateChanged:
            guard let userId = param?["userId"] as? String else { return }
            guard let hasVideo = param?["hasVideo"] as? Bool else { return }
            guard let streamType = param?["streamType"] as? TUIVideoStreamType else { return }
            guard userId == selectUserId, streamType == .cameraStream else { return }
            muteVideoItem.isSelect = !hasVideo
            viewResponder?.updateUI(item: muteVideoItem)
        case .onSendMessageForUserDisableChanged:
            guard let userId = param?["userId"] as? String else { return }
            guard let muted = param?["muted"] as? Bool else { return }
            guard userId == selectUserId else { return }
            muteMessageItem.isSelect = muted
            viewResponder?.updateUI(item: muteMessageItem)
        case .onSeatListChanged:
            if let seated = param?["seated"] as? [TUISeatInfo], seated.first(where: { $0.userId == selectUserId }) != nil {
                inviteSeatItem.isSelect = true
                viewResponder?.updateUI(item: inviteSeatItem)
                addViewItem(buttonItem: muteAudioItem, index: 0)
                addViewItem(buttonItem: muteVideoItem, index: 1)
            }
            if let left = param?["left"] as? [TUISeatInfo], left.first(where: { $0.userId == selectUserId }) != nil {
                inviteSeatItem.isSelect = false
                viewResponder?.updateUI(item: inviteSeatItem)
                removeViewItem(buttonType: .muteAudioItemType)
                removeViewItem(buttonType: .muteVideoItemType)
            }
        case .onUserInfoChanged:
            guard let modifyFlag = param?["modifyFlag"] as? TUIUserInfoModifyFlag, modifyFlag.contains(.userRole)  else { return }
            guard let userInfo = param?["userInfo"] as? TUIUserInfo else { return }
            guard userInfo.userId == currentUser.userId || userInfo.userId == selectUserId else { return }
            guard currentUser.userId != selectUserId else { return }
            guard let selectUserInfo = selectUserInfo else { return }
            userListManagerItems.removeAll()
            if currentUser.userRole == .roomOwner {
                createManagerItems()
                viewResponder?.updateStackView(items: userListManagerItems)
            } else if currentUser.userRole == .administrator, selectUserInfo.userRole == .generalUser {
                createManagerItems()
                viewResponder?.updateStackView(items: userListManagerItems)
            } else {
                viewResponder?.dismissView()
            }
        default: break
        }
    }
}

extension UserListManagerViewModel: UserNameInputViewResponder {
    func changeUserName(name: String) {
        engineManager.changeUserNameCard(userid: selectUserId, nameCard: name) { [weak self] in
            guard let self = self else { return }
            self.viewResponder?.makeToast(text: .nameChangedSuccessfully)
        } onError: { [weak self] _, _ in
            guard let self = self else { return }
            self.viewResponder?.makeToast(text: .nameChangeFailed)
        }
    }
    
    func onEndEditing() {
        viewResponder?.dismissView()
    }
}

private extension String {
    static var muteText: String {
        localized("Mute")
    }
    static var requestOpenVideoText: String {
        localized("Ask to start video")
    }
    static var requestOpenAudioText: String {
        localized("Ask to unmute")
    }
    static var closeVideoText: String {
        localized("Stop video")
    }
    static var changeHostText: String {
        localized("Make host")
    }
    static var muteMessageText: String {
        localized("Disable chat")
    }
    static var unMuteMessageText: String {
        localized("Enable chat")
    }
    static var kickOutRoomText: String {
        localized("Remove")
    }
    static var stepDownSeatText: String {
        localized("Leave the stage")
    }
    static var inviteSeatText: String {
        localized("Invite to stage")
    }
    static var invitedTakeSeatText: String {
        localized("The audience has been invited to the stage")
    }
    static var refusedTakeSeatInvitationText: String {
        localized("xx refused to go on stage")
    }
    static var takeSeatInvitationTimeoutText: String {
        localized("The invitation to xx to go on stage has timed out")
    }
    static var invitedOpenAudioText: String {
        localized("The audience has been invited to open the audio")
    }
    static var invitedOpenVideoText: String {
        localized("The audience has been invited to open the video")
    }
    static var kickOutText: String {
        localized("Do you want to move xx out of the conference?")
    }
    static var setAsAdministratorText: String {
        localized("Set as administrator")
    }
    static var undoAdministratorText: String {
        localized("Undo administrator")
    }
    static var haveTransferredMasterText: String {
        localized("The host has been transferred to xx")
    }
    static var setUpAdministratorText: String {
        localized("xx has been set as conference admin")
    }
    static var removedAdministratorText: String {
        localized("The conference admin status of xx has been withdrawn")
    }
    static var alertOkText: String {
        localized("OK")
    }
    static var cancelText: String {
        localized("Cancel")
    }
    static var transferHostTitle: String {
        localized("Transfer the host to xx")
    }
    static var transferHostMessage: String {
        localized("After transfer the host, you will become a general user")
    }
    static var transferHostsureText: String {
        localized("Confirm transfer")
    }
    static var receivedSameRequestText: String {
        localized("This member has already received the same request, please try again later")
    }
    static var onStageText: String {
        localized("xx is on stage")
    }
    static var theStageIsFullText: String {
        localized("The stage is full")
    }
    static let modifyTheName = localized("Modify the name")
    static let nameChangedSuccessfully = localized("Name changed successfully")
    static let nameChangeFailed = localized("Name change failed")
}
