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

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

/**
 * A request to provision a new Mesh node
 */
public class ProvisionNodeRequest {

    private final boolean authenticationEnabled;
    private final Path homeDir;
    private final String name;
    private final int port;
    private final int shardCount;

    private ProvisionNodeRequest(Builder builder) {
        authenticationEnabled = builder.authenticationEnabled;
        homeDir = builder.homeDir == null ? Paths.get(pathSafe(builder.name)) : builder.homeDir;
        name = builder.name;
        port = builder.port;
        shardCount = builder.shardCount;
    }

    /**
     * @return the new node's home directory; derived from the node {@link #getName() name} if not specified on the
     *         builder
     */
    @Nonnull
    public Path getHomeDir() {
        return homeDir;
    }

    /**
     * @return the new node's name
     */
    @Nonnull
    public String getName() {
        return name;
    }

    /**
     * @return the port on which the new node should service RPC requests; {@code 0} to pick an available port
     */
    public int getPort() {
        return port;
    }

    /**
     * @return the number of shards to create on the new node
     */
    public int getShardCount() {
        return shardCount;
    }

    /**
     * @return {@code true} if the provisioned node should have authentication enabled; otherwise, {@code false}
     */
    public boolean isAuthenticationEnabled() {
        return authenticationEnabled;
    }

    private static String pathSafe(String value) {
        return value.replaceAll("[^\\p{Alnum}\\-._]+", "_");
    }

    public static class Builder {

        private boolean authenticationEnabled;
        private Path homeDir;
        private String name;
        private int port;
        private int shardCount;

        public Builder() {
            name = UUID.randomUUID().toString();
        }

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

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

        @Nonnull
        public Builder homeDir(@Nullable Path value) {
            homeDir = value;
            return this;
        }

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

        @Nonnull
        public Builder port(int value) {
            port = value;
            return this;
        }

        @Nonnull
        public Builder shards(int value) {
            checkArgument(value > 0, "At least 1 shard is required");
            shardCount = value;
            return this;
        }
    }
}
