package no.valg.valgnatt.database;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

@Component
public class Configuration implements EnvironmentAware, InitializingBean {

    private Environment env;

    private String flywaySchemas;
    private String initialdata;
    private Set<String> betaPartier;
    private String[] frontendMediaURLer;
    private String[] frontendOffentligURLer;
    private String mottakFromURL;
    private boolean suspendAtStartup;
    private boolean nullstillingTillatt;
    private boolean ignorerUkjentePartier;

    @Override
    public void setEnvironment(final Environment env) {
        this.env = env;
    }

    @Override
    public void afterPropertiesSet() {
        flywaySchemas = getRequiredProperty("flyway.schemas", String.class);
        // Herfra hentes alle grunnlagsdata som skal brukes i valget
        initialdata = getRequiredProperty("pm.initialdata", String.class);
        // Her lister man partier om man har gode stemmetall på i mange valg bakover. Det er disse som brukes
        // i prognoseberegningen for å finne trendendringene, så vi kan se på disse partiene som "lyttepostene"
        // i prognosen. Alle andre partier blir også beregnet i prognosen, så det har ikke betydning for "om"
        // prognosen beregner et parti eller ikke. Mest sannsynlig skal man ikke endre noenting på denne listen heller.
        betaPartier = getStringSet("pm.betaPartier");
        // URL'er til frontend-servere som benyttes av media. URL'ene legges inn kommaseparert.
        // Dersom en klient har et opplegg for failover, så kan man legge inn flere URL'er mellom
        // hvert komma, da separert med +. Da vil den første URL'en bli forsøkt først, deretter
        // den andre, etc, om ikke den første svarer. Disse klientene vil motta rapporter så fort vi starter
        // opp frontend-servere. På valgnatten vil man starte opp media
        frontendMediaURLer = getStringArray("pm.frontendMediaURLer");
        // URL'er til frontend-servere som benyttes offentlig (valgresultat.no). URL'ene legges inn kommaseparert.
        // Dersom en klient har et opplegg for failover, så kan man legge inn flere URL'er mellom
        // hvert komma, da separert med +. Da vil den første URL'en bli forsøkt først, deretter
        // den andre, etc, om ikke den første svarer. Disse klientene vil motta rapporter så fort vi har
        // passert sperrefrist-tidspunktet (dvs. at Frontend-serveren vil begynne å motta rapporter fra Backend-serveren
        // etter dette tidspunktet)
        // Hvis man IKKE vil at Backend-serveren skal sende noen rapporter kan man sette "null" (uten fnutter) som verdi, for Wildfly aksepterer ikke en tom streng.
        frontendOffentligURLer = getStringArray("pm.frontendOffentligURLer");
        // Denne benyttes dersom man ønsker å kopiere innkommende annen valgnatt-instans, typisk
        // eksempel er å ville kjøre en "shadow"-instans av produksjon i sitt eget utviklingsmiljø
        // noe som er ekstremt verdifullt om det skulle oppstå grunn til å ville debugge/teste med
        // prod-like data
        // Hvis man IKKE vil at Backend-serveren skal sende noen rapporter kan man sette "null" (uten fnutter) som verdi, for Wildfly aksepterer ikke en tom streng.
        mottakFromURL = getOptionalProperty("pm.mottakFromURL", String.class, null);
        // Benyttes for å  sette opp en maskin som skal være "kald-failover". Ved oppstart så
        // kan en instans av valgnatt være i suspend-modus, men kan så aktiveres ved å sende
        // en kommando til ControlServlet (?setResume=true)
        suspendAtStartup = getOptionalProperty("pm.suspendAtStartup", Boolean.class, false);
        // Benyttes dersom man vil tillate å kunne nullstille database gjennom en kommando
        // mot ControlServlet. Dette er noe vi trenger for å kunne kjøre kontinuerlige simuleringer
        // med forskjellig varianter av stemmetall. NB!! Dette skal absolutt IKKE tillates i produksjon!!!
        nullstillingTillatt = getOptionalProperty("pm.nullstillingtillatt", Boolean.class, false);
        // Dersom vi sender inn stemmetall fra et gammelt valg til en nytt valggrunnlag, typisk
        // i forbindelse med en simulering, så kan vi ignorere partier i stemmetallsskjema som vi ikke
        // har i dette grunnlaget. Det kan gjøre det litt lettere å lage en slik simulering.
        ignorerUkjentePartier = getOptionalProperty("pm.ignorerukjentepartier", Boolean.class, false);
    }

    private String[] getStringArray(String key) {
        String property = getRequiredProperty(key, String.class);
        if ("".equals(property.trim()) || "null".equals(property)) {
            return new String[]{};
        }
        String[] arr = property.split("[,;]");
        Arrays.setAll(arr, i -> arr[i].trim());
        return arr;
    }

    private Set<String> getStringSet(String key) {
        Set<String> set = new HashSet<>();
        String[] requiredProperties = getRequiredProperty(key, String.class).split("[,;]");
        for (String requiredProperty : requiredProperties) {
            set.add(requiredProperty.trim());
        }
        return set;
    }

    private <T> T getRequiredProperty(String key, Class<T> targetType) {
        if (env.containsProperty(key)) {
            return env.getRequiredProperty(key, targetType);
        }
        return env.getRequiredProperty("global/" + key, targetType);
    }

    private <T> T getOptionalProperty(String key, Class<T> targetType, T defaultValue) {
        if (env.containsProperty(key)) {
            return env.getRequiredProperty(key, targetType);
        }
        T value = env.getProperty("global/" + key, targetType);
        return (value == null) ? defaultValue : value;
    }

    public String getFlywaySchemas() {
        return flywaySchemas;
    }

    public String getInitialdata() {
        return initialdata;
    }

    public void setInitialdata(String initialdata) {
        this.initialdata = initialdata;
    }

    public Set<String> getBetaPartier() {
        return betaPartier;
    }

    public String[] getFrontendMediaURLer() {
        return frontendMediaURLer;
    }

    public String[] getFrontendOffentligURLer() {
        return frontendOffentligURLer;
    }

    public String getMottakFromURL() {
        return mottakFromURL;
    }

    public boolean isSuspendAtStartup() {
        return suspendAtStartup;
    }

    public boolean erNullstillingTillatt() {
        return nullstillingTillatt;
    }

    public boolean ignorerUkjentePartier() {
        return ignorerUkjentePartier;
    }

    public void setIgnorerUkjentePartier(boolean ignorerUkjentePartier) {
        this.ignorerUkjentePartier = ignorerUkjentePartier;
    }
}
