package com.atlassian.bitbucket.mesh.transaction;

import com.atlassian.bitbucket.mesh.network.MeshNode;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcRepositoryConsistencySummary;
import com.atlassian.bitbucket.mesh.util.BuilderSupport;
import com.google.common.collect.ImmutableSet;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
import java.util.Set;

import static java.util.Objects.requireNonNull;

/**
 * @since 1.2
 */
public class RepositoryConsistencySummary {

    private final ReplicaVersion consensusVersion;
    private final Set<MeshNode> consistentReplicas;
    private final Set<MeshNode> inconsistentReplicas;
    private final Set<MeshNode> unavailableReplicas;

    private RepositoryConsistencySummary(Builder builder) {
        consensusVersion = builder.consensusVersion;
        consistentReplicas = builder.consistentReplicas.build();
        inconsistentReplicas = builder.inconsistentReplicas.build();
        unavailableReplicas = builder.unavailableReplicas.build();
    }

    @Nonnull
    public ReplicaVersion getConsensusVersion() {
        return consensusVersion;
    }

    @Nonnull
    public Set<MeshNode> getConsistentReplicas() {
        return consistentReplicas;
    }

    @Nonnull
    public Set<MeshNode> getInconsistentReplicas() {
        return inconsistentReplicas;
    }

    @Nonnull
    public Set<MeshNode> getUnavailableReplicas() {
        return unavailableReplicas;
    }

    @Nonnull
    public RpcRepositoryConsistencySummary toRpc() {
        RpcRepositoryConsistencySummary.Builder builder = RpcRepositoryConsistencySummary.newBuilder()
                .setContentHash(consensusVersion.getContentHash())
                .setVersion(consensusVersion.getVersion());
        consistentReplicas.forEach(node -> builder.addConsistentReplicas(node.getId()));
        inconsistentReplicas.forEach(node -> builder.addInconsistentReplicas(node.getId()));
        unavailableReplicas.forEach(node -> builder.addUnavailableReplicas(node.getId()));
        return builder.build();
    }

    public static class Builder extends BuilderSupport {

        private final ReplicaVersion consensusVersion;
        private final ImmutableSet.Builder<MeshNode> consistentReplicas;
        private final ImmutableSet.Builder<MeshNode> inconsistentReplicas;
        private final ImmutableSet.Builder<MeshNode> unavailableReplicas;

        public Builder(@Nonnull ReplicaVersion consensusVersion) {
            this.consensusVersion = requireNonNull(consensusVersion, "consensusVersion");

            consistentReplicas = ImmutableSet.builder();
            inconsistentReplicas = ImmutableSet.builder();
            unavailableReplicas = ImmutableSet.builder();
        }

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

        @Nonnull
        public Builder consistentReplicas(@Nullable MeshNode value, @Nullable MeshNode... moreValues) {
            addIf(Objects::nonNull, consistentReplicas, value, moreValues);
            return this;
        }

        @Nonnull
        public Builder inconsistentReplicas(@Nullable MeshNode value, @Nullable MeshNode... moreValues) {
            addIf(Objects::nonNull, inconsistentReplicas, value, moreValues);
            return this;
        }

        @Nonnull
        public Builder unavailableReplicas(@Nullable MeshNode value, @Nullable MeshNode... moreValues) {
            addIf(Objects::nonNull, unavailableReplicas, value, moreValues);
            return this;
        }
    }
}
