//
//  LNIMMessageData.swift
//  Lanu
//
//  Created by OneeChan on 2025/12/10.
//

import Foundation
import AutoCodable


enum LNIMMessageDataType {
    case none
    case official
    case system
    case text
    case image
    case voice
    case order
    case call(String)
    case autoReply(LNAutoReplyType)
    
    var isUserContent: Bool {
        switch self {
        case .text, .image, .voice: true
        default: false
        }
    }
}

enum LNIMMessageCustomType: String, Decodable {
    case none
    case official_image_text
    case playmate_order
    case playmate_welcome_text
    case playmate_welcome_voice
}

@AutoCodable
class LNIMCustomMessage: Decodable {
    var businessID: LNIMMessageCustomType = .none
}

extension SignalingActionType: @retroactive Decodable { }

@AutoCodable
class LNIMVoiceCallMessage: Decodable {
    var inviter: String = ""
    var actionType: SignalingActionType?
    var businessID: Int = 0
    var inviteID: String = ""
    var data: String = ""
    
    private var decodedData: LNIMVoiceCallData?
    
    var callData: LNIMVoiceCallData? {
        if let decodedData {
            return decodedData
        }
        
        guard let data = data.data(using: .utf8) else { return nil }
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .useDefaultKeys
        decodedData = try? decoder.decode(LNIMVoiceCallData.self, from: data)
        
        return decodedData
    }
    
    var contentDesc: String {
        guard let callData else { return "" }
        
        return if actionType == SignalingActionType.invite,
           let data = callData.data, data.cmd == "hangup" {
            .init(key: "C00001") + " " + callData.call_end.timeCountDisplay
        } else if actionType == SignalingActionType.cancel_Invite {
            if inviter.isMyUid {
                .init(key: "A00152")
            } else {
                .init(key: "A00142")
            }
        } else if actionType == SignalingActionType.reject_Invite {
            if callData.data?.cmd == "line_busy" {
                if inviter.isMyUid {
                    .init(key: "C00004")
                } else {
                    .init(key: "C00011")
                }
            } else {
                if inviter.isMyUid {
                    .init(key: "C00005")
                } else {
                    .init(key: "C00009")
                }
            }
        } else if actionType == SignalingActionType.invite_Timeout {
            if inviter.isMyUid {
                .init(key: "C00010")
            } else {
                .init(key: "C00002")
            }
        } else {
            ""
        }
    }
}

@AutoCodable
class LNIMVoiceCallData: Decodable {
    var call_end: Int = 0
    var data: LNIMVoiceCallExtraData?
    
    init() { }
}

@AutoCodable
class LNIMVoiceCallExtraData: Decodable {
    var excludeFromHistoryMessage: Bool = false
    var cmd: String = ""
}

@AutoCodable
class LNIMOfficialMessage: Decodable {
    var title: String = ""
    var content: String = ""
    var image: String = ""
    var link: String = ""
}

@AutoCodable
class LNIMOrderMessage: Decodable, LNOrderProtocol {
    var orderId: String = ""
    var bizCategoryName: String = ""
    var categoryIcon: String = ""
    var price: Double = 0.0
    var unit: String = ""
    var purchaseQty: Int = 0
    var status: LNOrderStatus = .created
    var createTime: Double = 0.0
    var star: Double = 0.0
    var refundApply: Bool = false
    var customerRemark: String = ""
    var buyerUserNo: String = ""
}

@AutoCodable
class LNAutoReplyTextMessage: Decodable {
    var textContent: String = ""
}

@AutoCodable
class LNAutoReplyVoiceMessage: Decodable {
    var voiceUrl: String = ""
    var voiceDuration: Int = 0
}

private extension V2TIMElemType {
    var toDataType: LNIMMessageDataType {
        switch self {
        case .ELEM_TYPE_TEXT: .text
        case .ELEM_TYPE_IMAGE: .image
        case .ELEM_TYPE_SOUND: .voice
        case .ELEM_TYPE_CUSTOM: .none
        default: .none
        }
    }
}

class LNIMMessageData: NSObject {
    let imMessage: V2TIMMessage
    private(set) var type: LNIMMessageDataType
    private(set) var content: String?
    private(set) var textContent: NSAttributedString?
    private(set) var voiceDuration: Int = 0
    
    private var customMessage: (any Decodable)?
    
    var readReceipt: V2TIMMessageReceipt?
    var isSelf: Bool
    
    init(imMessage: V2TIMMessage) {
        self.imMessage = imMessage
        self.type = imMessage.elemType.toDataType
        self.isSelf = imMessage.isSelf
        super.init()
        
        switch imMessage.elemType {
        case .ELEM_TYPE_TEXT: setupTextContent()
        case .ELEM_TYPE_IMAGE: setupImageContent()
        case .ELEM_TYPE_SOUND: setupVoiceContent()
        case .ELEM_TYPE_CUSTOM: setupCustomContent()
        default:
            break
        }
    }
    
    func decodeCustomMessage<T: Decodable>() -> T? {
        if let cached = customMessage as? T {
            return cached
        }
        
        guard let data = imMessage.customElem?.data else { return nil }
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .useDefaultKeys
        let result = try? decoder.decode(T.self, from: data)
        if let result {
            customMessage = result
        }
        return result
    }
    
    static func buildDateMessage(date: Date) -> LNIMMessageData {
        let data = LNIMMessageData(imMessage: V2TIMMessage())
        data.type = .system
        data.content = date.timeIntervalSince1970.tencentIMTimeDesc
        
        return data
    }
}

extension LNIMMessageData {
    private func setupTextContent() {
        content = imMessage.textElem?.text
        
        textContent = content?.getEmojiString(with: .body_l)
    }
    
    private func setupImageContent() {
        if imMessage.isSelf, let path = imMessage.imageElem?.path {
            if FileManager.default.fileExists(atPath: path) {
                content = path // 使用本地文件路径
                return
            } else if let fixedPath = path.fixedFilePath,
                        FileManager.default.fileExists(atPath: fixedPath) {
                content = fixedPath // UUID 变化后的本地文件路径
                return
            }
        }
        content = imMessage.imageElem?.imageList.first(where: { $0.type == .IMAGE_TYPE_THUMB })?.url
    }
    
    private func setupVoiceContent() {
        content = imMessage.soundElem?.path
        voiceDuration = Int(imMessage.soundElem?.duration ?? 0)
    }
    
    private func setupCustomContent() {
        guard let data = imMessage.customElem?.data else { return }
        
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .useDefaultKeys // 处理蛇形命名
        
        // 判断是否是通话消息
        if let voiceCall = try? decoder.decode(LNIMVoiceCallMessage.self, from: data),
           voiceCall.actionType != nil, voiceCall.businessID != 0 {
            type = .call(voiceCall.inviteID)
            return
        }
        
        // 判断是否是自定义消息
        if let result = try? decoder.decode(LNIMCustomMessage.self, from: data) {
            switch result.businessID {
            case .playmate_order:
                type = .order // 订单消息
            case .official_image_text:
                type = .official // 官方消息
            case .playmate_welcome_text:
                type = .autoReply(.text)
                if let message: LNAutoReplyTextMessage = decodeCustomMessage() {
                    textContent = NSMutableAttributedString(string: message.textContent)
                }
            case .playmate_welcome_voice:
                type = .autoReply(.voice)
                if let message: LNAutoReplyVoiceMessage = decodeCustomMessage() {
                    content = message.voiceUrl
                    voiceDuration = message.voiceDuration
                }
            case .none: type = .none
            }
            return
        }
        type = .none
    }
}
