package com.atlassian.bitbucket.mesh.config;

import com.atlassian.bitbucket.mesh.transaction.QuorumMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.core.env.Environment;

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

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

public class DefaultReplicationConfig implements ReplicationConfig {

    private static final Logger log = LoggerFactory.getLogger(DefaultReplicationConfig.class);

    private final Environment environment;

    private volatile Settings current;

    DefaultReplicationConfig(Environment environment) {
        this.environment = environment;

        reload();
    }

    @Override
    public double getContentHashForcedRecalculationFrequency() {
        return current.contentHashForcedRecalculationFrequency;
    }

    @Nonnull
    @Override
    public Duration getFinalizeInterval() {
        return current.finalizeInterval;
    }

    @Nonnull
    @Override
    public QuorumMode getQuorumMode() {
        return current.quorumMode;
    }

    @Nonnull
    @Override
    public Duration getYieldTokenRetryInterval() {
        return current.yieldTokenRetryInterval;
    }

    void reload() {
        current = Binder.get(environment).bindOrCreate("replication", Settings.class);
    }

    private static class Settings {

        final double contentHashForcedRecalculationFrequency;
        final Duration finalizeInterval;
        final QuorumMode quorumMode;
        final Duration yieldTokenRetryInterval;

        @ConstructorBinding
        public Settings(@DefaultValue("0.01") double contentHashForcedRecalculationFrequency,
                        @DefaultValue("5") @DurationUnit(ChronoUnit.MINUTES) Duration finalizeInterval,
                        @DefaultValue("unanimous") QuorumMode quorumMode,
                        @DefaultValue("30") @DurationUnit(ChronoUnit.SECONDS) Duration yieldTokenRetryInterval) {
            this.contentHashForcedRecalculationFrequency =
                    parseContentHashForcedRecalculationFrequency(contentHashForcedRecalculationFrequency);
            this.finalizeInterval = finalizeInterval.toMinutes() < 1L ? Duration.ofMinutes(1L) : finalizeInterval;
            this.quorumMode = quorumMode;
            this.yieldTokenRetryInterval = yieldTokenRetryInterval;
        }

        private static double parseContentHashForcedRecalculationFrequency(double value) {
            if (value < 0.0d || value > 1.0d) {
                double fallback = 0.01d;
                log.warn("[replication] Invalid value '{}' specified for forced content hash recalculation " +
                                "frequency ('{}' property). The value must be between 0.0 inclusive and 1.0 " +
                                "inclusive. Defaulting to '{}' ('{}%').",
                        value, REPLICATION_CONTENT_HASH_FORCED_RECALCULATION_FREQUENCY, fallback, fallback * 100);
                return fallback;
            }
            return value;
        }
    }
}
