package com.atlassian.bitbucket.mesh.config;

import org.springframework.core.env.Environment;

import javax.annotation.Nonnull;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;

import static com.atlassian.bitbucket.mesh.rpc.ManagedConfigurationProperties.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

class DefaultGrpcConfig implements GrpcConfig {

    private static final String PROP_MAX_PROGRESS_UPDATE_FREQUENCY = "grpc.progress.update.max.frequency";
    private static final String PROP_SERVER_ADDRESS = "grpc.server.address";
    private static final String PROP_SERVER_PORT = "grpc.server.port";
    private static final String PROP_SERVER_SSL_CERT_CHAIN_PATH = "grpc.server.ssl.cert-chain-path";
    private static final String PROP_SERVER_SSL_PRIVATE_KEY_PATH = "grpc.server.ssl.private-key-path";

    private final Environment environment;

    private volatile Settings current;

    DefaultGrpcConfig(Environment environment) {
        this.environment = environment;
        current = new Settings(environment);
    }

    @Override
    public long getFragmentSize() {
        return current.fragmentSize;
    }

    @Nonnull
    @Override
    public Duration getKeepAliveTime() {
        return current.keepAliveTime;
    }

    @Override
    public long getMaxFragmentSize() {
        return current.maxFragmentSize;
    }

    @Override
    public int getMaxMessageSize() {
        return current.maxMessageSize;
    }

    @Override
    public int getMaxMetadataSize() {
        return current.maxMetadataSize;
    }

    @Override
    public int getMaxProgressUpdateFrequency() {
        return current.maxProgressUpdateFrequency;
    }

    @Nonnull
    @Override
    public String getServerAddress() {
        return current.serverAddress;
    }

    @Override
    public int getServerPort() {
        return current.serverPort;
    }

    @Nonnull
    @Override
    public Duration getServerShutdownTimeout() {
        return current.serverShutdownTimeout;
    }

    @Override
    public Path getServerSslCertChainPath() {
        return current.serverSslCertChainPath;
    }

    @Override
    public Path getServerSslPrivateKeyPath() {
        return current.serverSslPrivateKeyPath;
    }

    @Override
    public boolean isKeepAliveWithoutCallsAllowed() {
        return current.keepAliveWithoutCallsAllowed;
    }

    @Override
    public boolean isServerRestartOnConfigurationChange() {
        return current.serverRestartOnConfigurationChange;
    }

    @Override
    public boolean isZeroCopy() {
        return current.zeroCopy;
    }

    void reload() {
        current = new Settings(environment);
    }

    private static class Settings {

        private final long fragmentSize;
        private final Duration keepAliveTime;
        private final boolean keepAliveWithoutCallsAllowed;
        private final long maxFragmentSize;
        private final int maxMessageSize;
        private final int maxMetadataSize;
        private final int maxProgressUpdateFrequency;
        private final String serverAddress;
        private final int serverPort;
        private final boolean serverRestartOnConfigurationChange;
        private final Duration serverShutdownTimeout;
        private final Path serverSslCertChainPath;
        private final Path serverSslPrivateKeyPath;
        private final boolean zeroCopy;

        Settings(Environment environment) {
            keepAliveTime = Duration.ofSeconds(environment.getProperty(GRPC_SERVER_KEEP_ALIVE_TIME, Long.class, 30L));
            keepAliveWithoutCallsAllowed =
                    environment.getProperty(GRPC_SERVER_KEEP_ALIVE_WITHOUT_CALLS, Boolean.class, true);
            maxMessageSize = Math.max(1048576, // at least 1Mb
                    environment.getProperty(GRPC_MAX_MESSAGE_SIZE, Integer.class, 4194304)); // 4Mb
            fragmentSize = Math.min(maxMessageSize - 1024, // at most maxMessageSize (with some padding)
                    Math.max(32768L, // at least 32kb
                            environment.getProperty(GRPC_FRAGMENT_SIZE, Long.class, 131072L))); // 128kb
            maxFragmentSize = Math.min(maxMessageSize - 1024, // at most maxMessageSize (with some padding)
                    Math.max(fragmentSize, // at least fragmentSize
                            environment.getProperty(GRPC_MAX_FRAGMENT_SIZE, Long.class, 3145728L))); // 3Mb
            maxMetadataSize = Math.max(8192, // at least 8k
                    environment.getProperty(GRPC_MAX_METADATA_SIZE, Integer.class, 65536)); //64k
            maxProgressUpdateFrequency =
                    environment.getProperty(PROP_MAX_PROGRESS_UPDATE_FREQUENCY, Integer.class, 4); // 4Hz
            serverRestartOnConfigurationChange = environment.getProperty(
                    GRPC_SERVER_RESTART_ON_CONFIG_CHANGE, Boolean.class, Boolean.FALSE);
            serverAddress = trimToEmpty(environment.getProperty(PROP_SERVER_ADDRESS, String.class, "0.0.0.0"));
            serverPort = environment.getProperty(PROP_SERVER_PORT, Integer.class, 7777);
            serverShutdownTimeout = Duration.ofSeconds(Math.max(0,
                    environment.getProperty(GRPC_SERVER_SHUTDOWN_TIMEOUT, Integer.class, 30)));
            String sslCertChainPath = environment.getProperty(PROP_SERVER_SSL_CERT_CHAIN_PATH);
            String sslPrivateKeyPath = environment.getProperty(PROP_SERVER_SSL_PRIVATE_KEY_PATH);
            serverSslCertChainPath = isNotBlank(sslCertChainPath) ? Paths.get(sslCertChainPath) : null;
            serverSslPrivateKeyPath = isNotBlank(sslPrivateKeyPath) ? Paths.get(sslPrivateKeyPath) : null;
            zeroCopy = environment.getProperty(GRPC_ZERO_COPY, Boolean.class, true);
        }
    }
}
