package com.atlassian.bitbucket.mesh.config;

import com.atlassian.bitbucket.mesh.git.binary.GitBinaryHelper;
import com.atlassian.bitbucket.mesh.layout.StorageLayout;
import com.atlassian.bitbucket.mesh.util.ExpressionUtils;
import org.springframework.core.env.Environment;

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

import static com.atlassian.bitbucket.mesh.rpc.ManagedConfigurationProperties.PROCESS_TIMEOUT_EXECUTION;
import static com.atlassian.bitbucket.mesh.rpc.ManagedConfigurationProperties.PROCESS_TIMEOUT_IDLE;

public class DefaultApplicationConfig implements ApplicationConfig {

    private static final String PROP_EXECUTOR_MAX_THREADS = "executor.max-threads";
    private static final String PROP_EXECUTOR_TIMEOUT = "executor.timeout";

    private final DefaultAuthenticationConfig authenticationConfig;
    private final Environment environment;
    private final DefaultGitConfig gitConfig;
    private final DefaultGrpcConfig grpcConfig;
    private final DefaultPackCacheConfig hostingCacheConfig;
    private final DefaultLfsConfig lfsConfig;
    private final DefaultLoggingConfig loggingConfig;
    private final DefaultNodeConfig nodeConfig;
    private final DefaultProcessConfig processConfig;
    private final DefaultProfilingConfig profilingConfig;
    private final DefaultRepairConfig repairConfig;
    private final DefaultReplicationConfig replicationConfig;
    private final DefaultStorageConfig storageConfig;
    private final DefaultTranscodeConfig transcodeConfig;

    private volatile Settings current;

    public DefaultApplicationConfig(Environment environment, GitBinaryHelper gitBinaryHelper,
                                    StorageLayout storageLayout) {
        this.environment = environment;

        authenticationConfig = new DefaultAuthenticationConfig(environment);
        current = new Settings(environment);
        gitConfig = new DefaultGitConfig(environment, gitBinaryHelper);
        grpcConfig = new DefaultGrpcConfig(environment);
        hostingCacheConfig = new DefaultPackCacheConfig(environment, storageLayout.getCacheDir());
        lfsConfig = new DefaultLfsConfig(environment);
        loggingConfig = new DefaultLoggingConfig(environment);
        nodeConfig = new DefaultNodeConfig(environment);
        processConfig = new DefaultProcessConfig(environment);
        profilingConfig = new DefaultProfilingConfig(environment);
        repairConfig = new DefaultRepairConfig(environment);
        replicationConfig = new DefaultReplicationConfig(environment);
        storageConfig = new DefaultStorageConfig(environment);
        transcodeConfig = new DefaultTranscodeConfig(environment);
    }

    @Nonnull
    @Override
    public AuthenticationConfig getAuthentication() {
        return authenticationConfig;
    }

    @Nonnull
    @Override
    public Duration getDefaultExecutionTimeout() {
        return current.defaultExecutionTimeout;
    }

    @Nonnull
    @Override
    public Duration getDefaultIdleTimeout() {
        return current.defaultIdleTimeout;
    }

    @Override
    public int getExecutorMaxThreads() {
        String value = environment.getProperty(PROP_EXECUTOR_MAX_THREADS, "cpu");
        return Math.max(4, ExpressionUtils.parseExpressionAsInt(value)
                .orElse(Runtime.getRuntime().availableProcessors()));
    }

    @Nonnull
    @Override
    public Duration getExecutorTimeout() {
        return current.executorTimeout;
    }

    @Nonnull
    @Override
    public GitConfig getGit() {
        return gitConfig;
    }

    @Nonnull
    @Override
    public GrpcConfig getGrpc() {
        return grpcConfig;
    }

    @Nonnull
    @Override
    public LfsConfig getLfs() {
        return lfsConfig;
    }

    @Nonnull
    @Override
    public LoggingConfig getLogging() {
        return loggingConfig;
    }

    @Nonnull
    @Override
    public NodeConfig getNode() {
        return nodeConfig;
    }

    @Nonnull
    @Override
    public PackCacheConfig getPackCache() {
        return hostingCacheConfig;
    }

    @Nonnull
    @Override
    public ProcessConfig getProcess() {
        return processConfig;
    }

    @Nonnull
    @Override
    public ProfilingConfig getProfiling() {
        return profilingConfig;
    }

    @Nonnull
    @Override
    public RepairConfig getRepair() {
        return repairConfig;
    }

    @Nonnull
    @Override
    public ReplicationConfig getReplication() {
        return replicationConfig;
    }

    @Nonnull
    @Override
    public StorageConfig getStorageConfig() {
        return storageConfig;
    }

    @Nonnull
    @Override
    public TranscodeConfig getTranscode() {
        return transcodeConfig;
    }

    @Override
    public void reload() {
        current = new Settings(environment);

        authenticationConfig.reload();
        gitConfig.reload();
        grpcConfig.reload();
        hostingCacheConfig.reload();
        lfsConfig.reload();
        loggingConfig.reload();
        nodeConfig.reload();
        processConfig.reload();
        profilingConfig.reload();
        repairConfig.reload();
        replicationConfig.reload();
        storageConfig.reload();
        transcodeConfig.reload();
    }

    private static class Settings {

        private final Duration defaultExecutionTimeout;
        private final Duration defaultIdleTimeout;
        private final Duration executorTimeout;

        Settings(Environment environment) {
            defaultExecutionTimeout = Duration.ofSeconds(
                    environment.getProperty(PROCESS_TIMEOUT_EXECUTION, Long.class, 120L));
            defaultIdleTimeout = Duration.ofSeconds(
                    environment.getProperty(PROCESS_TIMEOUT_IDLE, Long.class, 60L));
            executorTimeout = Duration.ofSeconds(
                    Math.max(0L, environment.getProperty(PROP_EXECUTOR_TIMEOUT, Long.class, 60L)));
        }
    }
}
