import Libbox
import Library
import SwiftUI

@MainActor
public class GroupListViewModel: BaseViewModel {
    @Published public var groups: [OutboundGroup] = []

    private var pendingSelections: [String: String] = [:]

    override public init() {
        super.init()
        isLoading = true
    }

    public func connect() {
        if Variant.screenshotMode {
            groups = [
                OutboundGroup(tag: "my_group", type: "selector", selected: "server", selectable: true, isExpand: true, items: [
                    OutboundGroupItem(tag: "server", type: "Shadowsocks", urlTestTime: .now, urlTestDelay: 10),
                    OutboundGroupItem(tag: "server2", type: "WireGuard", urlTestTime: .now, urlTestDelay: 20),
                    OutboundGroupItem(tag: "auto", type: "URLTest", urlTestTime: .now, urlTestDelay: 30),
                ]),
                OutboundGroup(tag: "Auto", type: "urltest", selected: "Tokyo", selectable: true, isExpand: false, items: [
                    OutboundGroupItem(tag: "Tokyo", type: "Shadowsocks", urlTestTime: .now, urlTestDelay: 10),
                    OutboundGroupItem(tag: "Singapore", type: "VMess", urlTestTime: .now, urlTestDelay: 20),
                    OutboundGroupItem(tag: "Hong Kong", type: "Trojan", urlTestTime: .now, urlTestDelay: 15),
                ]),
            ]
            isLoading = false
        }
    }

    public func setGroups(_ goGroups: [LibboxOutboundGroup]?) {
        guard let goGroups else { return }

        let existingGroups = Dictionary(uniqueKeysWithValues: groups.map { ($0.tag, $0) })

        var newGroups = [OutboundGroup]()
        for goGroup in goGroups {
            var items = [OutboundGroupItem]()
            let itemIterator = goGroup.getItems()!
            while itemIterator.hasNext() {
                let goItem = itemIterator.next()!
                items.append(OutboundGroupItem(
                    tag: goItem.tag,
                    type: goItem.type,
                    urlTestTime: Date(timeIntervalSince1970: Double(goItem.urlTestTime)),
                    urlTestDelay: UInt16(goItem.urlTestDelay)
                ))
            }

            var selected = goGroup.selected
            if let pending = pendingSelections[goGroup.tag] {
                if goGroup.selected == pending {
                    pendingSelections.removeValue(forKey: goGroup.tag)
                } else {
                    selected = pending
                }
            }

            let isExpand = existingGroups[goGroup.tag]?.isExpand ?? goGroup.isExpand

            newGroups.append(OutboundGroup(
                tag: goGroup.tag,
                type: goGroup.type,
                selected: selected,
                selectable: goGroup.selectable,
                isExpand: isExpand,
                items: items
            ))
        }
        groups = newGroups
        isLoading = false
    }

    public func selectOutbound(groupTag: String, outboundTag: String) {
        if let index = groups.firstIndex(where: { $0.tag == groupTag }) {
            groups[index].selected = outboundTag
        }
        pendingSelections[groupTag] = outboundTag

        Task {
            await doSelectOutbound(groupTag: groupTag, outboundTag: outboundTag)
        }
    }

    private nonisolated func doSelectOutbound(groupTag: String, outboundTag: String) async {
        do {
            try await LibboxNewStandaloneCommandClient()!.selectOutbound(groupTag, outboundTag: outboundTag)
        } catch {
            await MainActor.run {
                alert = AlertState(action: "select outbound", error: error)
            }
        }
    }

    public func toggleExpand(groupTag: String) {
        guard let index = groups.firstIndex(where: { $0.tag == groupTag }) else { return }
        groups[index].isExpand.toggle()
        let isExpand = groups[index].isExpand
        Task {
            await setGroupExpand(tag: groupTag, isExpand: isExpand)
        }
    }

    private nonisolated func setGroupExpand(tag: String, isExpand: Bool) async {
        do {
            try await LibboxNewStandaloneCommandClient()!.setGroupExpand(tag, isExpand: isExpand)
        } catch {
            await MainActor.run {
                alert = AlertState(action: "update group expansion", error: error)
            }
        }
    }

    public func performURLTest(_ tag: String) {
        Task {
            await doURLTest(tag: tag)
        }
    }

    private nonisolated func doURLTest(tag: String) async {
        do {
            try await LibboxNewStandaloneCommandClient()!.urlTest(tag)
        } catch {
            await MainActor.run {
                alert = AlertState(action: "run URL test", error: error)
            }
        }
    }
}
