package com.atlassian.bitbucket.mesh.test.cluster;

import com.atlassian.bitbucket.mesh.util.BuilderSupport;
import com.google.common.collect.ImmutableList;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
 * A node in the Mesh cluster.
 */
public abstract class AbstractMeshClusterNode implements MeshClusterNode {

    protected String id;

    private final String name;
    private final List<Path> shards;
    private final String url;

    protected AbstractMeshClusterNode(AbstractBuilder<?, ?> builder) {
        shards = builder.shards.build();
        id = builder.id;
        name = builder.name;
        url = requireNonNull(builder.url, "url");
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof AbstractMeshClusterNode)) {
            return false;
        }
        AbstractMeshClusterNode that = (AbstractMeshClusterNode) o;
        return name.equals(that.name);
    }

    @Override
    public String getId() {
        return id;
    }

    @Nonnull
    @Override
    public String getName() {
        return name;
    }

    @Nonnull
    @Override
    public List<Path> getShards() {
        return shards;
    }

    @Nonnull
    @Override
    public String getUrl() {
        return url;
    }

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

    protected abstract static class AbstractBuilder<B extends AbstractBuilder<B, T>, T> extends BuilderSupport {

        private final ImmutableList.Builder<Path> shards;

        private String id;
        private String name;
        private String url;

        public AbstractBuilder(@Nonnull String name) {
            this.name = requireNonBlank(name, "name");

            shards = ImmutableList.builder();
        }

        public AbstractBuilder(@Nonnull AbstractMeshClusterNode other) {
            this(requireNonNull(other, "other").getName());

            shards.addAll(other.getShards());
            url = other.url;
        }

        @Nonnull
        public abstract T build();

        @Nonnull
        public B id(@Nullable String value) {
            id = value;
            return self();
        }

        @Nonnull
        public B name(@Nonnull String value) {
            name = requireNonBlank(value, "name");
            return self();
        }

        @Nonnull
        public B shards(@Nullable Path value, @Nullable Path... moreValues) {
            addIf(Objects::nonNull, shards, value, moreValues);
            return self();
        }

        @Nonnull
        public B url(@Nonnull String value) {
            url = requireNonBlank(value, "url");
            return self();
        }

        protected abstract B self();
    }
}
