import Foundation

struct XMLElement: Stateable, CustomStringConvertible {
    private(set) var name: String
    private(set) var xmlns: String?
    private(set) var attributes: [String: String]
    private(set) var content: String?
    private(set) var nodes: [XMLElement]
    var woClose: Bool = false

    mutating func updateXmlns(_ new: String?) {
        xmlns = new
    }

    mutating func updateContent(_ new: String?) {
        guard let new = new else { return }

        if var content = content {
            content += new
            self.content = content
        } else {
            content = new
        }
    }

    mutating func addNode(_ new: XMLElement) {
        nodes.append(new)
    }

    var stringRepresentation: String {
        var result = "<\(name)"
        for (key, value) in attributes {
            let val = XMLEscaping.escape(value)
            result += " \(key)='\(val)'"
        }
        if let xmlns {
            result += " xmlns='\(xmlns)'"
        }
        if !nodes.isEmpty {
            result += ">"
            for child in nodes {
                result += child.stringRepresentation
            }
            result += "</\(name)>"
        } else {
            if let content = content {
                result += ">"
                result += content
                result += "</\(name)>"
            } else {
                result += woClose ? ">" : "/>"
            }
        }
        return result
    }

    var description: String {
        stringRepresentation
    }
}

extension XMLElement {
    static var randomId: String {
        let letters = "abcdefghijklmnopqrstuvwxyz0123456789"
        return String((0..<8).map { _ in letters.randomElement() ?? "x" })
    }
}
