import Foundation

// MARK: - Widget Type

enum WidgetType: Int, Sendable {
    case button = 0
    case textField = 1
    case checkBox = 2
    case listBox = 3
    case slider = 4
    case treeView = 5
    case multiSelectList = 6
}

// MARK: - Tree Node

struct RemoteTreeNode: Identifiable, Sendable {
    let id: String
    let text: String
    let children: [RemoteTreeNode]
    let isExpanded: Bool

    init?(from value: MsgPackValue) {
        guard let id = value[0]?.stringValue,
              let text = value[1]?.stringValue else { return nil }
        self.id = id
        self.text = text
        self.children = value[2]?.arrayValue?.compactMap { RemoteTreeNode(from: $0) } ?? []
        self.isExpanded = value[3]?.boolValue ?? false
    }
}

// MARK: - Widget State

@Observable
final class WidgetState: Identifiable, @unchecked Sendable {
    let id: String
    var label: String
    var enabled: Bool
    var type: WidgetType

    // TextField
    var text: String = ""
    var isPassword: Bool = false
    var maxLength: Int = 1_000_000
    var readOnly: Bool = false

    // CheckBox
    var isChecked: Bool = false

    // ListBox
    var items: [String] = []
    var selectedIndex: Int = -1

    // Slider
    var sliderValue: Int = 0
    var minValue: Int = 0
    var maxValue: Int = 100
    var increment: Int = 1

    // TreeView
    var nodes: [RemoteTreeNode] = []
    var selectedNodeId: String = ""

    // MultiSelectList
    var selectedIndices: Set<Int> = []

    init(id: String, label: String, enabled: Bool, type: WidgetType) {
        self.id = id
        self.label = label
        self.enabled = enabled
        self.type = type
    }

    /// Parse a widget from a MessagePack Union value: [typeId, objectMap]
    static func fromUnion(_ value: MsgPackValue) -> WidgetState? {
        guard let arr = value.arrayValue, arr.count == 2,
              let typeId = arr[0].intValue,
              let type = WidgetType(rawValue: typeId) else { return nil }

        let obj = arr[1]
        guard let id = obj[0]?.stringValue else { return nil }
        let label = obj[1]?.stringValue ?? ""
        let enabled = obj[2]?.boolValue ?? true

        let widget = WidgetState(id: id, label: label, enabled: enabled, type: type)

        switch type {
        case .button:
            break
        case .textField:
            widget.text = obj[5]?.stringValue ?? ""
            widget.isPassword = obj[6]?.boolValue ?? false
            widget.maxLength = obj[7]?.intValue ?? 1_000_000
            widget.readOnly = obj[8]?.boolValue ?? false
        case .checkBox:
            widget.isChecked = obj[5]?.boolValue ?? false
        case .listBox:
            widget.items = obj[5]?.arrayValue?.compactMap(\.stringValue) ?? []
            widget.selectedIndex = obj[6]?.intValue ?? -1
        case .slider:
            widget.sliderValue = obj[5]?.intValue ?? 0
            widget.minValue = obj[6]?.intValue ?? 0
            widget.maxValue = obj[7]?.intValue ?? 100
            widget.increment = obj[8]?.intValue ?? 1
        case .treeView:
            widget.nodes = obj[5]?.arrayValue?.compactMap { RemoteTreeNode(from: $0) } ?? []
            widget.selectedNodeId = obj[6]?.stringValue ?? ""
        case .multiSelectList:
            widget.items = obj[5]?.arrayValue?.compactMap(\.stringValue) ?? []
            let indices = obj[6]?.arrayValue?.compactMap(\.intValue) ?? []
            widget.selectedIndices = Set(indices)
        }

        return widget
    }

    /// Update this widget from a new Union value (preserving identity)
    func update(from value: MsgPackValue) {
        guard let arr = value.arrayValue, arr.count == 2 else { return }
        let obj = arr[1]

        label = obj[1]?.stringValue ?? label
        enabled = obj[2]?.boolValue ?? enabled

        if let typeId = arr[0].intValue, let newType = WidgetType(rawValue: typeId) {
            type = newType
        }

        switch type {
        case .button:
            break
        case .textField:
            if let t = obj[5]?.stringValue { text = t }
            if let p = obj[6]?.boolValue { isPassword = p }
            if let m = obj[7]?.intValue { maxLength = m }
            if let r = obj[8]?.boolValue { readOnly = r }
        case .checkBox:
            if let c = obj[5]?.boolValue { isChecked = c }
        case .listBox:
            if let i = obj[5]?.arrayValue { items = i.compactMap(\.stringValue) }
            if let s = obj[6]?.intValue { selectedIndex = s }
        case .slider:
            if let v = obj[5]?.intValue { sliderValue = v }
            if let mn = obj[6]?.intValue { minValue = mn }
            if let mx = obj[7]?.intValue { maxValue = mx }
            if let inc = obj[8]?.intValue { increment = inc }
        case .treeView:
            if let n = obj[5]?.arrayValue { nodes = n.compactMap { RemoteTreeNode(from: $0) } }
            if let s = obj[6]?.stringValue { selectedNodeId = s }
        case .multiSelectList:
            if let i = obj[5]?.arrayValue { items = i.compactMap(\.stringValue) }
            if let s = obj[6]?.arrayValue { selectedIndices = Set(s.compactMap(\.intValue)) }
        }
    }
}

// MARK: - Remote Form

@Observable
final class RemoteFormData: Identifiable, @unchecked Sendable {
    let id: String
    var title: String
    var widgets: [WidgetState]

    init(id: String, title: String, widgets: [WidgetState]) {
        self.id = id
        self.title = title
        self.widgets = widgets
    }

    static func from(_ value: MsgPackValue) -> RemoteFormData? {
        guard let id = value[0]?.stringValue,
              let title = value[1]?.stringValue else { return nil }

        let widgets = value[2]?.arrayValue?.compactMap { WidgetState.fromUnion($0) } ?? []
        return RemoteFormData(id: id, title: title, widgets: widgets)
    }

    func updateWidgets(from value: MsgPackValue) {
        // value[1] is UpdatedWidgets: Dictionary<string, RemoteWidget?>
        guard let updates = value[1] else { return }

        if case .map(let pairs) = updates {
            for (key, val) in pairs {
                guard let widgetId = key.stringValue else { continue }

                if val.isNil {
                    // Remove widget
                    widgets.removeAll { $0.id == widgetId }
                } else if let existing = widgets.first(where: { $0.id == widgetId }) {
                    // Update existing widget
                    existing.update(from: val)
                } else if let newWidget = WidgetState.fromUnion(val) {
                    // Add new widget
                    widgets.append(newWidget)
                }
            }
        }
    }
}
