package com.atlassian.bitbucket.mesh.config;

import com.atlassian.bitbucket.mesh.util.ExpressionUtils;
import com.google.common.base.Strings;
import org.springframework.core.env.Environment;

import javax.annotation.Nonnull;
import java.time.Duration;

import static com.atlassian.bitbucket.mesh.rpc.ManagedConfigurationProperties.HOOKS_BUFFER_CAPACITY;
import static com.atlassian.bitbucket.mesh.rpc.ManagedConfigurationProperties.HOOKS_CALLBACK_TIMEOUT;

class DefaultHooksConfig implements HooksConfig {

    static final String PROP_NATIVE_CALLBACK = "hooks.native-callback";
    static final String PROP_PACK_OBJECTS_TIMEOUT = "hooks.pack-objects.timeout";
    static final String PROP_SOCKET_ADDRESS = "hooks.socket.address";
    static final String PROP_SOCKET_PORT = "hooks.socket.port";
    static final String PROP_THREADS_CORE = "hooks.threads.core";
    static final String PROP_THREADS_KEEP_ALIVE = "hooks.threads.keep-alive";
    static final String PROP_THREADS_MAX = "hooks.threads.max";

    private final Environment environment;

    private volatile Settings current;

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

    @Override
    public long getBufferCapacity() {
        return current.bufferCapacity;
    }

    @Nonnull
    @Override
    public Duration getCallbackTimeout() {
        return current.callbackTimeout;
    }

    @Nonnull
    @Override
    public Duration getPackObjectsTimeout() {
        return current.packObjectsTimeout;
    }

    @Override
    public String getSocketAddress() {
        return current.socketAddress;
    }

    @Override
    public int getSocketPort() {
        return current.socketPort;
    }

    @Override
    public int getThreadsCore() {
        return current.threadsCore;
    }

    @Nonnull
    @Override
    public Duration getThreadsKeepAlive() {
        return current.threadsKeepAlive;
    }

    @Override
    public int getThreadsMax() {
        return current.threadsMax;
    }

    @Override
    public boolean isNativeCallbackEnabled() {
        return current.nativeCallback;
    }

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

    private static class Settings {

        private final long bufferCapacity;
        private final Duration callbackTimeout;
        private final boolean nativeCallback;
        private final Duration packObjectsTimeout;
        private final String socketAddress;
        private final int socketPort;
        private final int threadsCore;
        private final Duration threadsKeepAlive;
        private final int threadsMax;

        Settings(Environment environment) {
            //at least 32kb
            bufferCapacity = Math.max(32768L, environment.getProperty(HOOKS_BUFFER_CAPACITY, Long.class, 5242880L));
            callbackTimeout = Duration.ofSeconds(environment.getProperty(HOOKS_CALLBACK_TIMEOUT, Long.class, 240L));
            nativeCallback = environment.getProperty(PROP_NATIVE_CALLBACK, Boolean.class, Boolean.TRUE);
            packObjectsTimeout = Duration.ofSeconds(
                    environment.getProperty(PROP_PACK_OBJECTS_TIMEOUT, Long.class, 86400L));
            socketAddress = Strings.emptyToNull(environment.getProperty(PROP_SOCKET_ADDRESS, String.class));
            socketPort = Math.max(0, environment.getProperty(PROP_SOCKET_PORT, Integer.class, 0));
            threadsCore = ExpressionUtils.parseExpressionAsInt(
                    environment.getProperty(PROP_THREADS_CORE, String.class, "2"))
                    .orElse(2);
            threadsKeepAlive = Duration.ofMinutes(environment.getProperty(PROP_THREADS_KEEP_ALIVE, Long.class, 5L));
            threadsMax = ExpressionUtils.parseExpressionAsInt(
                    environment.getProperty(PROP_THREADS_MAX, String.class, "8*cpu"))
                    .orElse(Integer.MAX_VALUE);
        }
    }
}
