package com.atlassian.bitbucket.mesh.network;

import com.atlassian.bitbucket.mesh.rpc.v1.RpcNode;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;

public class MeshNode {

    private final String id;
    private final boolean local;
    private final String name;
    private final String rpcUrl;

    private MeshNode(Builder builder) {
        id = builder.id;
        local = builder.local;
        name = builder.name;
        rpcUrl = requireNonNull(builder.rpcUrl, "rpcUrl");
    }

    @Override
    public boolean equals(@Nullable Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        MeshNode that = (MeshNode) o;
        return id.equals(that.id);
    }

    /**
     * @return the node's identifier
     */
    @Nonnull
    public String getId() {
        return id;
    }

    /**
     * @return the node's human readable name
     */
    @Nullable
    public String getName() {
        return name;
    }

    /**
     * @return the URL on which the node's RPC services are available
     */
    @Nonnull
    public String getRpcUrl() {
        return rpcUrl;
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    /**
     * @return {@code true} if the instance represents this Mesh instance, otherwise {@code false}
     */
    public boolean isLocal() {
        return local;
    }

    /**
     * @return {@code true} if the instance represents a remote Mesh node
     */
    public boolean isRemote() {
        return !local;
    }

    @Nonnull
    public RpcNode toNode() {
        return RpcNode.newBuilder()
                .setName(defaultString(name))
                .setId(id)
                .setRpcUrl(rpcUrl)
                .build();
    }

    @Override
    public String toString() {
        String result = (isBlank(name) ? rpcUrl : name) + "@" + id;
        return local ? result + "*" : result;
    }

    public static class Builder {

        private final String id;

        private boolean local;
        private String name;
        private String rpcUrl;

        public Builder(@Nonnull String id) {
            this.id = requireNonNull(id, "id");
        }

        public Builder(@Nonnull MeshNode other) {
            this(requireNonNull(other, "other").getId());

            local = other.isLocal();
            name = other.getName();
            rpcUrl = other.getRpcUrl();
        }

        @Nonnull
        public MeshNode build() {
            return new MeshNode(this);
        }

        @Nonnull
        public Builder local(boolean value) {
            local = value;
            return this;
        }

        @Nonnull
        public Builder name(@Nullable String value) {
            name = value;
            return this;
        }

        @Nonnull
        public Builder rpcUrl(@Nonnull String value) {
            rpcUrl = requireNonNull(value, "rpcUrl");
            return this;
        }
    }
}
