public struct JID: Stateable, Hashable, CustomStringConvertible {
    public let localPart: String
    public let domainPart: String
    public let resourcePart: String?

    public init(_ jid: String) throws {
        let parts = try JID.parse(jid)
        localPart = parts.0
        domainPart = parts.1
        resourcePart = parts.2
    }

    public var description: String {
        var str = "\(localPart)@\(domainPart)"
        if let resource = resourcePart {
            str += "/\(resource)"
        }
        return str
    }

    var hash: Int {
        if let resourcePart {
            return "\(localPart)@\(domainPart)/\(resourcePart)".hashValue
        } else {
            return "\(localPart)@\(domainPart)".hashValue
        }
    }
}

extension JID {
    var uStr: String {
        "\(hash)".replacingOccurrences(of: "-", with: "_")
    }
}

enum JIDError: String, Error {
    case wrongJid = "Can't parse or operate with JID"
}

// swiftlint:disable large_tuple
private extension JID {
    static func parse(_ str: String) throws -> (String, String, String?) {
        let parts = str.components(separatedBy: "@")
        guard parts.count == 2 else {
            throw JIDError.wrongJid
        }
        let secondParts = parts[1].components(separatedBy: "/")
        guard !secondParts.isEmpty else {
            throw JIDError.wrongJid
        }
        if secondParts.count > 1 {
            return (parts[0], secondParts[0], secondParts[1])
        } else {
            return (parts[0], secondParts[0], nil)
        }
    }
}
