public struct JID: Hashable, CustomStringConvertible, Codable, Equatable {
    let localPart: String
    let domainPart: String
    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 full: String {
        var str = "\(localPart)@\(domainPart)"
        if let resource = resourcePart {
            str += "/\(resource)"
        }
        return str
    }

    public var description: String {
        full
    }

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

    public var bare: String {
        "\(localPart)@\(domainPart)"
    }

    public var bareHash: Int {
        "\(localPart)@\(domainPart)".hashValue
    }
}

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

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

private extension JID {
    // swiftlint:disable:next large_tuple
    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)
        }
    }
}