//
//  ContactResources.swift
//  Monal
//
//  Created by Friedrich Altheide on 24.12.21.
//  Copyright © 2021 Monal.im. All rights reserved.
//
import OrderedCollections

@ViewBuilder
func resourceRowElement(_ k: String, _ v: some View, space: CGFloat = 5) -> some View {
    HStack {
        Text(k).font(.headline)
        Spacer()
        v.foregroundColor(.secondary)
    }
}

struct ContactResources: View {
    @StateObject var contact: ObservableKVOWrapper<MLContact>
    @State var contactVersionInfos: [String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>]
    @State private var showCaps: String?

    init(contact: ObservableKVOWrapper<MLContact>, previewMock: [String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>]? = nil) {
        _contact = StateObject(wrappedValue: contact)
        
        if previewMock != nil {
            self.contactVersionInfos = previewMock!
        } else {
            var tmpInfos:[String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>] = [:]
            for ressourceName in DataLayer.sharedInstance().resources(for: contact.obj) {
                // load already known software version info from database
                if let softwareInfo = DataLayer.sharedInstance().getSoftwareVersionInfo(forContact:contact.obj.contactJid, resource:ressourceName, andAccount:contact.obj.accountID) {
                    tmpInfos[ressourceName] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(softwareInfo)
                }
            }
            self.contactVersionInfos = tmpInfos
        }
    }

    var body: some View {
        List {
            ForEach(self.contactVersionInfos.sorted(by:{ $0.0 < $1.0 }), id: \.key) { key, versionInfo in
                Section {
                    VStack {
                        resourceRowElement("Resource:", Text(versionInfo.resource as String))
                        resourceRowElement("Client Name:", Text(versionInfo.appName as String? ?? ""))
                        resourceRowElement("Client Version:", Text(versionInfo.appVersion as String? ?? ""))
                        resourceRowElement("OS:", Text(versionInfo.platformOs as String? ?? ""))
                        if let lastInteraction = versionInfo.lastInteraction as Date? {
                            if lastInteraction.timeIntervalSince1970 == 0 {
                                resourceRowElement("Last Interaction:", Text("Currently Online"))
                            } else {
                                resourceRowElement("Last Interaction:", Text(lastInteraction.formatted(date:.numeric, time:.standard)))
                            }
                        } else {
                            resourceRowElement("Last Interaction:", Text("unsupported"))
                        }
                    }
                    .onTapGesture(count: 2, perform: {
                        showCaps = versionInfo.resource
                    })
                }
            }
        }
        .richAlert(isPresented:$showCaps, title:Text("XMPP Capabilities")) { resource in
            VStack(alignment: .leading) {
                Text("The resource '\(resource)' has the following capabilities:")
                    .font(Font.body.weight(.semibold))
                Spacer()
                    .frame(height: 20)
                Section {
                    let capsVer = DataLayer.sharedInstance().getVerForUser(self.contact.contactJid, andResource:resource, onAccountID:self.contact.accountID)
                    Text("Caps hash: \(String(describing:capsVer))")
                    Divider()
                    if let capsSet = DataLayer.sharedInstance().getCapsforVer(capsVer, onAccountID:contact.obj.accountID) as? Set<String> {
                        let caps = Array(capsSet)
                        VStack(alignment: .leading) {
                            ForEach(caps, id: \.self) { cap in
                                Text(cap)
                                    .font(.system(.footnote, design:.monospaced))
                                if cap != caps.last {
                                    Divider()
                                }
                            }
                        }
                    }
                }
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalXmppUserSoftWareVersionRefresh")).receive(on: RunLoop.main)) { notification in
            if let xmppAccount = notification.object as? xmpp, let softwareInfo = notification.userInfo?["versionInfo"] as? MLContactSoftwareVersionInfo {
                DDLogVerbose("Got software version info from account \(xmppAccount)...")
                if softwareInfo.fromJid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID {
                    DispatchQueue.main.async {
                        DDLogVerbose("Successfully matched software version info update to current contact: \(contact.obj)")
                        self.contactVersionInfos[softwareInfo.resource ?? ""] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(softwareInfo)
                    }
                }
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalNewPresenceNotice")).receive(on: RunLoop.main)) { notification in
            if let xmppAccount = notification.object as? xmpp, let jid = notification.userInfo?["jid"] as? String, let resource = notification.userInfo?["resource"] as? String, let available = notification.userInfo?["available"] as? NSNumber {
                DDLogVerbose("Got presence update from account \(xmppAccount)...")
                if jid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID {
                    DispatchQueue.main.async {
                        DDLogVerbose("Successfully matched presence update to current contact: \(contact.obj)")
                        if available.boolValue {
                            if let softwareInfo = DataLayer.sharedInstance().getSoftwareVersionInfo(forContact:contact.obj.contactJid, resource:resource, andAccount:contact.obj.accountID) {
                                self.contactVersionInfos[resource] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(softwareInfo)
                            }
                            // query software version from contact
                            MLXMPPManager.sharedInstance().getEntitySoftWareVersion(for:contact.obj, andResource:resource)
                        } else {
                            self.contactVersionInfos[resource] = nil
                        }
                    }
                }
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalLastInteractionUpdatedNotice")).receive(on: RunLoop.main)) { notification in
            if let xmppAccount = notification.object as? xmpp, let jid = notification.userInfo?["jid"] as? String, let resource = notification.userInfo?["resource"] as? String, notification.userInfo?["lastInteraction"] as? NSDate != nil {
                DDLogVerbose("Got lastInteraction update from account \(xmppAccount)...")
                if jid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID {
                    DispatchQueue.main.async {
                        DDLogVerbose("Successfully matched lastInteraction update to current contact: \(contact.obj)")
                        self.contactVersionInfos[resource]?.obj.lastInteraction = DataLayer.sharedInstance().lastInteraction(ofJid:self.contact.obj.contactJid, andResource:resource, forAccountID:contact.obj.accountID)
                    }
                }
            }
        }
        .onAppear {
            DDLogVerbose("View will appear...")
            let newTimeout = DispatchTime.now() + 1.0;
            DispatchQueue.main.asyncAfter(deadline: newTimeout) {
                DDLogVerbose("Refreshing software version info...")
                for ressourceName in DataLayer.sharedInstance().resources(for: contact.obj) {
                    // query software version from contact
                    MLXMPPManager.sharedInstance().getEntitySoftWareVersion(for:contact.obj, andResource:ressourceName)
                }
            }
        }
        .navigationBarTitle(Text("Devices of \(contact.contactDisplayName as String)"), displayMode: .inline)
    }
}

func previewMock() -> [String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>] {
    var previewMock:[String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>] = [:]
    previewMock["m1"] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(MLContactSoftwareVersionInfo.init(jid: "test1@monal.im", andRessource: "m1", andAppName: "Monal", andAppVersion: "1.1.1", andPlatformOS: "ios", andLastInteraction: Date()))
    previewMock["m2"] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(MLContactSoftwareVersionInfo.init(jid: "test1@monal.im", andRessource: "m2", andAppName: "Monal", andAppVersion: "1.1.2", andPlatformOS: "macOS", andLastInteraction: Date()))
    previewMock["m3"] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(MLContactSoftwareVersionInfo.init(jid: "test1@monal.im", andRessource: "m3", andAppName: "Monal", andAppVersion: "1.1.2", andPlatformOS: "macOS", andLastInteraction: Date()))
    return previewMock
}

struct ContactResources_Previews: PreviewProvider {
    static var previews: some View {
        ContactResources(contact:ObservableKVOWrapper<MLContact>(MLContact.makeDummyContact(0)), previewMock:previewMock())
    }
}