//
//  LNIMChatViewModel.swift
//  Lanu
//
//  Created by OneeChan on 2025/12/11.
//

import Foundation
import Combine


enum LNIMChatDataSourceChangeType {
    case insert
    case delete
    case reload
}


protocol LNIMChatViewModelNotify {
    func onIMMessageDataChanged(viewModel: LNIMChatViewModel, index: Int,
                                type: LNIMChatDataSourceChangeType, toBottom: Bool)
    func onIMMessageDatasChanged(viewModel: LNIMChatViewModel, indexs: [IndexPath],
                                 type: LNIMChatDataSourceChangeType, toBottom: Bool)
    func onIMDidSendMessage(to uid: String)
}
extension LNIMChatViewModelNotify {
    func onIMMessageDataChanged(viewModel: LNIMChatViewModel, index: Int,
                                type: LNIMChatDataSourceChangeType, toBottom: Bool) { }
    func onIMMessageDatasChanged(viewModel: LNIMChatViewModel, indexs: [IndexPath],
                                 type: LNIMChatDataSourceChangeType, toBottom: Bool) { }
    func onIMDidSendMessage(to uid: String) { }
}


class LNIMChatViewModel: NSObject {
    let userId: String
    @Published
    private(set) var userInfo: LNUserProfileVO?
    @Published
    private(set) var remark: String?
    
    // 消息
    private var loading = false
    private var topMessage: V2TIMMessage?
    private(set) var allMessage: [LNIMMessageData] = []
    private var lastData: Date? = nil
    
    private var orderMessageCache: [String: LNIMMessageData] = [:]
    private var callMessageCache: [String: LNIMMessageData] = [:]
    
    // 配置
    @Published
    private(set) var messageOpt: V2TIMReceiveMessageOpt = .RECEIVE_MESSAGE
    
    @Published
    private(set) var myOrders: [LNUnfinishedOrderVO] = []
    @Published
    private(set) var peerSkills: [LNGameMateSkillVO] = []
    
    var needMarkAutoReply = false
    
    init(userId: String) {
        self.userId = userId
        super.init()
        V2TIMManager.sharedInstance().addAdvancedMsgListener(listener: self)
        
        if !userId.isImOfficialId {
            loadUserInfo()
            getUserRemark()
            getUnfinishOrder()
        }
        LNEventDeliver.addObserver(self)
        
        V2TIMManager.sharedInstance().subscribeUserStatus(userIDList: [userId], succ: nil)
    }
    
    func cleanUnread() {
        LNIMManager.shared.cleanConversationUnread(uid: userId)
    }
}

extension LNIMChatViewModel {
    func getUnfinishOrder() {
        var nextTag: String = ""
        var orders: [LNUnfinishedOrderVO] = []
        
        func _loadOrder() {
            LNOrderManager.shared.getUnfinishedOrderWith(uid: userId, size: 30, next: nextTag)
            { [weak self] list, next in
                guard let self else { return }
                if let list, let next {
                    orders.append(contentsOf: list.filter({
                        $0.status == .created
                        || $0.status == .waitingForAccept
                        || $0.status == .accepted
                        || $0.status == .servicing
                    }))
                    nextTag = next
                }
                
                if next?.isEmpty == false {
                    _loadOrder()
                } else {
                    myOrders = orders
                }
            }
        }
        _loadOrder()
    }
    
    private func loadUserInfo() {
        LNProfileManager.shared.getUserProfileDetail(uid: userId) { [weak self] info in
            guard let self else { return }
            guard let info else { return }
            userInfo = info
            if info.playmate {
                loadSkills()
            }
        }
    }
    
    private func loadSkills() {
        LNGameMateManager.shared.getUserSkills(uid: userId) { [weak self] skills in
            guard let self else { return }
            guard let skills else { return }
            peerSkills = skills
        }
    }
    
    private func getUserRemark() {
        LNIMManager.shared.getUsersRemark(uids: [userId]) { [weak self] remarks in
            guard let self else { return }
            guard let remarks, !remarks.isEmpty else { return }
            remark = remarks.first(where: { $0.userNo == self.userId })?.note
        }
    }
}

// MARK: 消息管理
extension LNIMChatViewModel {
    func updateMessageOpt(opt: V2TIMReceiveMessageOpt) {
        V2TIMManager.sharedInstance().setC2CReceiveMessageOpt(userIDList: [userId], opt: opt) { [weak self] in
            guard let self else { return }
            messageOpt = opt
        }
    }
    
    func updateRemark(_ remark: String) {
        LNIMManager.shared.setUserRemark(uid: userId, remark: remark) { [weak self] success in
            guard let self else { return }
            guard success else { return }
            self.remark = remark
        }
    }
    
    private func getMessageOpt() {
        V2TIMManager.sharedInstance().getC2CReceiveMessageOpt(userIDList: [userId]) { [weak self] infos in
            guard let self else { return }
            guard let infos, !infos.isEmpty else { return }
            messageOpt = infos.first(where: { $0.userID == self.userId })?.receiveOpt ?? .RECEIVE_MESSAGE
        } fail: { _, _ in }
    }
}

// MARK: 消息发送
extension LNIMChatViewModel {
    func sendTextMessage(text: String) {
        guard !text.isEmpty else { return }
        guard let message = V2TIMManager.sharedInstance().createTextMessage(text: text) else { return }
        sendMessage(message: message)
    }
    
    func sendVoiceMessage(voicePath: String, duration: Double) {
        guard !voicePath.isEmpty,
              FileManager.default.fileExists(atPath: voicePath) else {
            // MARK: 文件有效性判断
            return
        }
        guard duration > 1.0 else {
            // MARK: 最短限制
            return
        }
        guard let message = V2TIMManager.sharedInstance().createSoundMessage(
            audioFilePath: voicePath,
            duration: Int32(ceil(duration))) else { return }
        sendMessage(message: message)
    }
    
    func sendImageMessage(image: UIImage) {
        guard let imageData = image.jpegData(compressionQuality: 1.0) else { return }
        
        try? FileManager.default.createDirectory(
            at: URL.imageCacheFolder,
            withIntermediateDirectories: true,
            attributes: nil
        )
        let path = URL.imageCacheFolder.appendingPathComponent("\(curTimeInMicro).jpeg")
        do {
            try imageData.write(to: path, options: .atomic)
            guard let message = V2TIMManager.sharedInstance().createImageMessage(imagePath: path.path) else { return }
            sendMessage(message: message)
        } catch {
            return
        }
    }
    
    func resendMessage(message: LNIMMessageData) {
        guard message.imMessage.isSelf,
              message.imMessage.status == .MSG_STATUS_SEND_FAIL else { return }
        guard let index = allMessage.firstIndex(of: message) else { return }
        allMessage.remove(at: index)
        notifyMessageChanged(index: index, type: .delete, toBottom: false)
        sendMessage(message: message.imMessage)
    }
    
    private func sendMessage(message: V2TIMMessage) {
        if needMarkAutoReply {
            LNGameMateManager.shared.markUseAutoReplay(uid: userId)
            needMarkAutoReply = false
        }
        
        message.needReadReceipt = true
        let datas = transUIMsgFromIMMsg(messages: [message])
        guard !datas.isEmpty else { return }
        
        if allMessage.isEmpty {
            topMessage = message
        }
        allMessage.append(contentsOf: datas)
        notifyMessageChanged(index: allMessage.count - 1, type: .insert, toBottom: true)
        
        let pushInfo = V2TIMOfflinePushInfo()
        pushInfo.title = myUserInfo.nickname
        V2TIMManager.sharedInstance().sendMessage(
            message: message, receiver: userId,
            groupID: "", priority: .PRIORITY_NORMAL,
            onlineUserOnly: false, offlinePushInfo: pushInfo,
            progress: nil)
        { [weak self] in
            guard let self else { return }
            if let index = allMessage.firstIndex(of: datas.last!) {
                notifyMessageChanged(index: index, type: .reload, toBottom: false)
            }
            LNEventDeliver.notifyEvent { ($0 as? LNIMChatViewModelNotify)?.onIMDidSendMessage(to: self.userId) }
            
            if let last = allMessage.filter({ $0.isSelf && $0.type.isUserContent }).suffix(5).first,
               Int(curTime - (last.imMessage.timestamp?.timeIntervalSince1970 ?? 0)) <= 10 * 60 {
                let event = LNReportEvent()
                event.event = .deep_chat_hit
                event.scene = .playmate_deep_chat_report
                event.uid = userId
                LNReportManager.shared.reportEvent(event: event)
            }
        } fail: { [weak self] code, err in
           guard let self else { return }
            if let index = allMessage.firstIndex(of: datas.last!) {
                notifyMessageChanged(index: index, type: .reload, toBottom: false)
            }
            if code == LNIMCustomErrorCode.inBlackList.rawValue {
                showToast(.init(key: "A00087"))
            } else if code == LNIMCustomErrorCode.userNotExist.rawValue {
                showToast(.init(key: "A00088"))
            } else {
                showToast(err?.description)
            }
        }
    }
}

// MARK: 消息拉取
extension LNIMChatViewModel {
    func loadNextPage(handler: @escaping (_ success: Bool, _ isFirst: Bool) -> Void) {
        guard !loading else {
            handler(false, false)
            return
        }
        loading = true
        
        V2TIMManager.sharedInstance().getC2CHistoryMessageList(
            userID: userId, count: 300,
            lastMsg: topMessage)
        { [weak self] list in
            guard let self else { return }
            
            let isFirst = allMessage.isEmpty
            guard let list, !list.isEmpty else {
                loading = false
                handler(false, isFirst)
                return
            }
            
            topMessage = list.last
            
            let messages = transUIMsgFromIMMsg(messages: list)
            
            allMessage.insert(contentsOf: messages, at: 0)
            
            loading = false
            handler(true, isFirst)
            
            getMessageReadReceipts(list: list)
        } fail: { [weak self] code, err in
            guard let self else { return }
            handler(false, false)
            loading = false
        }
    }
    
    private func getMessageReadReceipts(list: [V2TIMMessage]) {
        V2TIMManager.sharedInstance().getMessageReadReceipts(messageList: list) { [weak self] receipts in
            guard let self else { return }
            guard let receipts, !receipts.isEmpty else { return }
            var messageIndex: [IndexPath] = []
            receipts.forEach { item in
                if let index = self.allMessage.firstIndex(where: { $0.imMessage.msgID == item.msgID }) {
                    self.allMessage[index].readReceipt = item
                    messageIndex.append(.init(row: index, section: 0))
                }
            }
            notifyMessagesChanged(indexs: messageIndex, type: .reload, toBottom: false)
        } fail: { code, err in }
    }
}

// MARK: 消息处理
extension LNIMChatViewModel {
    private func transUIMsgFromIMMsg(messages: [V2TIMMessage]) -> [LNIMMessageData] {
        var datas: [LNIMMessageData] = []
        
        for message in messages.reversed() {
            // 被标记不展示
            if message.isExcludedFromLastMessage || message.isExcludedFromUnreadCount {
                continue
            }
            
            let data = LNIMMessageData(imMessage: message)
            if case .order = data.type {
                guard let orderMessage: LNIMOrderMessage = data.decodeCustomMessage() else {
                    continue // 解析失败，忽略
                }
                if let oldMessage = orderMessageCache[orderMessage.orderId] {
                    // 存在旧的订单信息
                    if (oldMessage.imMessage.timestamp?.timeIntervalSince1970 ?? 0)
                        > (message.timestamp?.timeIntervalSince1970 ?? 0) {
                        // 消息为旧的订单信息，忽略
                        continue
                    } else {
                        // 订单的新消息
                        removeOldMessage(oldMessage)
                        if let index = datas.firstIndex(of: oldMessage) {
                            datas.remove(at: index)
                        }
                    }
                }
                orderMessageCache[orderMessage.orderId] = data
            } else if case .call(let callId) = data.type {
                guard let callMessage: LNIMVoiceCallMessage = data.decodeCustomMessage() else {
                    continue // 解析失败，忽略
                }
                
                // ->>>>> ⚠️ 挂断自己发起的通话，消息发送者会变成对方
                // ->>>>> ⚠️ 这里需要纠正发送者
                data.isSelf = callMessage.inviter.isMyUid
                // ->>>>> ⚠️
                
                if let oldMessage = callMessageCache[callId] {
                    // 存在旧的订单信息
                    if (oldMessage.imMessage.timestamp?.timeIntervalSince1970 ?? 0)
                        > (message.timestamp?.timeIntervalSince1970 ?? 0) {
                        // 消息为旧的订单信息，忽略
                        continue
                    } else {
                        // 订单的新消息
                        removeOldMessage(oldMessage)
                        if let index = datas.firstIndex(of: oldMessage) {
                            datas.remove(at: index)
                        }
                    }
                }
                callMessageCache[callId] = data
            }
            
            datas.append(data)
        }
        
        datas.forEach {
            guard let index = datas.firstIndex(of: $0) else { return }
            
            if let dateMessage = buildDateMessageIfNeed(message: $0.imMessage) {
                datas.insert(dateMessage, at: index)
            }
        }
        
        return datas
    }
    
    private func removeOldMessage(_ message: LNIMMessageData) {
        guard let index = allMessage.firstIndex(of: message) else { return }
        
        // 移除旧的订单消息
        var removeIndexs: [IndexPath] = []
        allMessage.remove(at: index)
        removeIndexs.append(.init(row: index, section: 0))
        
        if index - 1 >= 0 && index <= allMessage.count - 1,
           case .system = allMessage[index - 1].type,
           case .system = allMessage[index].type {
            // 出现连续的两个时间戳消息，需要将前一个移除
            allMessage.remove(at: index - 1)
            removeIndexs.append(.init(row: index - 1, section: 0))
        }
        
        notifyMessagesChanged(indexs: removeIndexs, type: .delete, toBottom: false)
    }
    
    private func buildDateMessageIfNeed(message: V2TIMMessage) -> LNIMMessageData? {
        guard let time = message.timestamp else { return nil }
            if let lastData,
            time.timeIntervalSince(lastData) < 5 * 60 {
            return nil
        }
        
        lastData = message.timestamp
        return LNIMMessageData.buildDateMessage(date: time)
    }
}

// MARK: 接收消息推送
extension LNIMChatViewModel: V2TIMAdvancedMsgListener {
    func onRecvNewMessage(msg: V2TIMMessage!) {
        guard msg.userID == userId else { return }
        
        let list = transUIMsgFromIMMsg(messages: [msg])
        if list.isEmpty { return }
        
        allMessage.append(contentsOf: list)
        notifyMessageChanged(index: allMessage.count - 1, type: .insert, toBottom: true)
        
        var isOrderMessage = false
        for item in list {
            if case .order = item.type {
                isOrderMessage = true
                break
            }
        }
        if isOrderMessage {
            getUnfinishOrder()
        }
    }
    
    func onRecvMessageReadReceipts(receiptList: [V2TIMMessageReceipt]!) {
        var indexs: [Int] = []
        receiptList.forEach { item in
            if let index = allMessage.firstIndex(where: { $0.imMessage.msgID == item.msgID }) {
                allMessage[index].readReceipt = item
                indexs.append(index)
            }
        }
        notifyMessagesChanged(indexs: indexs.map({ .init(row: $0, section: 0) }), type: .reload, toBottom: false)
    }
}

// MARK: 对外通知
extension LNIMChatViewModel {
    private func notifyMessageChanged(index: Int, type: LNIMChatDataSourceChangeType, toBottom: Bool) {
        LNEventDeliver.notifyEvent {
            ($0 as? LNIMChatViewModelNotify)?.onIMMessageDataChanged(viewModel: self, index: index, type: type, toBottom: toBottom)
        }
    }
    
    private func notifyMessagesChanged(indexs: [IndexPath], type: LNIMChatDataSourceChangeType, toBottom: Bool) {
        LNEventDeliver.notifyEvent {
            ($0 as? LNIMChatViewModelNotify)?.onIMMessageDatasChanged(viewModel: self, indexs: indexs, type: type, toBottom: toBottom)
        }
    }
}
