package com.atlassian.bitbucket.mesh.config;

import com.atlassian.bitbucket.mesh.git.binary.GitBinary;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.repository.SharedObjectsRepository;
import org.springframework.core.env.Environment;

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

import static com.atlassian.bitbucket.mesh.rpc.ManagedConfigurationProperties.*;

class DefaultGcConfig implements GcConfig {

    private final Environment environment;

    //The current gc settings are kept in a separate object so they can be easily reloaded
    private volatile Settings current;

    DefaultGcConfig(GitBinary binary, Environment environment) {
        this.environment = environment;

        current = new Settings(environment);
    }

    @Nonnull
    @Override
    public Duration getDelay() {
        return current.delay;
    }

    @Nonnull
    @Override
    public Duration getFullVacuumInterval() {
        return current.fullVacuumInterval;
    }

    @Override
    public int getFullCommitGraphThreshold() {
        return current.fullCommitGraphThreshold;
    }

    @Nonnull
    @Override
    public Duration getLogRetention() {
        return current.logRetention;
    }

    @Override
    public int getMaxAttempts() {
        return current.maxAttempts;
    }

    @Nonnull
    @Override
    public Duration getPackRefsInterval() {
        return current.packRefsInterval;
    }

    @Nonnull
    @Override
    public Duration getPackRefsTimeout() {
        return current.packRefsTimeout;
    }

    @Nonnull
    @Override
    public Duration getPruneExpire(@Nonnull Repository repository) {
        if (repository instanceof SharedObjectsRepository) {
            return current.pruneExpireSharedObjects;
        }
        return current.pruneExpireDefault;
    }

    @Nonnull
    @Override
    public Duration getPruneExpireDefault() {
        return current.pruneExpireDefault;
    }

    @Nonnull
    @Override
    public Duration getPruneExpireSharedObjects() {
        return current.pruneExpireSharedObjects;
    }

    @Nonnull
    @Override
    public Duration getRepackInterval() {
        return current.repackInterval;
    }

    @Override
    public int getRepackThresholdLoose() {
        return current.repackThresholdLoose;
    }

    @Override
    public long getRepackThresholdLooseSize() {
        return current.repackThresholdLooseSize;
    }

    @Override
    public int getRepackThresholdPack() {
        return current.repackThresholdPack;
    }

    @Nonnull
    @Override
    public Duration getRepackTimeout() {
        return current.repackTimeout;
    }

    @Override
    public int getThreads() {
        return current.threads;
    }

    @Nonnull
    @Override
    public Duration getVacuumInterval() {
        return current.vacuumInterval;
    }

    @Override
    public boolean isConvertStashRefs() {
        return current.convertStashRefs;
    }

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

    private static class Settings {

        private final boolean convertStashRefs;
        private final Duration delay;
        private final Duration fullVacuumInterval;
        private final int fullCommitGraphThreshold;
        private final Duration logRetention;
        private final int maxAttempts;
        private final Duration packRefsInterval;
        private final Duration packRefsTimeout;
        private final Duration pruneExpireDefault;
        private final Duration pruneExpireSharedObjects;
        private final Duration repackInterval;
        private final int repackThresholdLoose;
        private final long repackThresholdLooseSize;
        private final int repackThresholdPack;
        private final Duration repackTimeout;
        private final int threads;
        private final Duration vacuumInterval;

        Settings(Environment environment) {
            convertStashRefs = environment.getProperty(GC_CONVERT_STASH_REFS, Boolean.class, true);
            delay = Duration.ofSeconds(
                    Math.max(30, environment.getProperty(GC_DELAY, Long.class, 60L))); // min 30s
            fullCommitGraphThreshold = environment.getProperty(GC_COMMIT_GRAPH_FULL_THRESHOLD, Integer.class, 8);
            fullVacuumInterval = Duration.ofMinutes(environment.getProperty(GC_FULL_VACUUM_INTERVAL, Long.class, 10080L));
            logRetention = Duration.ofDays(
                    Math.max(0, environment.getProperty(GC_LOG_RETENTION, Long.class, 30L)));
            maxAttempts = environment.getProperty(GC_MAX_ATTEMPTS, Integer.class, 3);
            packRefsInterval = Duration.ofMinutes(
                    Math.max(5, environment.getProperty(GC_PACKREFS_INTERVAL, Long.class, 60L))); // min 5m
            packRefsTimeout = Duration.ofSeconds(
                    Math.max(120, environment.getProperty(GC_PACKREFS_TIMEOUT, Long.class, 600L))); // min 2m
            pruneExpireDefault = Duration.ofHours(
                    Math.max(4, environment.getProperty(GC_PRUNE_EXPIRE_DEFAULT, Long.class, 72L))); // min 4h
            pruneExpireSharedObjects = Duration.ofHours(
                    Math.max(24, environment.getProperty(GC_PRUNE_EXPIRE_SHARED_OBJECTS, Long.class, 168L))); // min 1d
            repackInterval = Duration.ofMinutes(environment.getProperty(GC_REPACK_INTERVAL, Long.class, 960L));
            repackThresholdLoose = environment.getProperty(GC_REPACK_THRESHOLD_LOOSE, Integer.class, 6700);
            repackThresholdLooseSize = Math.max(26214400L,
                    environment.getProperty(GC_REPACK_THRESHOLD_LOOSE_SIZE, Long.class, 268435456L)); // min 25M
            repackThresholdPack = environment.getProperty(GC_REPACK_THRESHOLD_PACK, Integer.class, 50);
            repackTimeout = Duration.ofSeconds(
                    Math.max(300, environment.getProperty(GC_REPACK_TIMEOUT, Long.class, 172800L))); // min 5m
            threads = environment.getProperty(GC_THREADS, Integer.class, 3);
            vacuumInterval = Duration.ofMinutes(environment.getProperty(GC_VACUUM_INTERVAL, Long.class, 960L));
        }
    }
}
