package no.valg.valgnatt.database;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import no.valg.valgnatt.common.ValgnattException;
import no.valg.valgnatt.database.domene.ValgType;
import no.valg.valgnatt.database.domene.ValgTypeAar;
import no.valg.valgnatt.database.geografi.Fylke;
import no.valg.valgnatt.database.geografi.Kommune;
import no.valg.valgnatt.database.geografi.Land;
import no.valg.valgnatt.database.hibernate.Partiliste;
import no.valg.valgnatt.database.hibernate.PartilisteId;
import no.valg.valgnatt.database.hibernate.Stemmekrets;
import no.valg.valgnatt.database.hibernate.Valgdistrikt;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

@Component
public class GrunnlagsdataCache {

    private static final Logger LOG = LogManager.getLogger(GrunnlagsdataCache.class);
    @Resource
    private GrunnlagsdataService grunnlagsdataService;
    @Resource
    private Configuration configuration;
    private Map<ValgTypeAar, Land> geografier = new TreeMap<>();
    private Map<PartilisteId, Partiliste> partilister = new TreeMap<>();

    private SisteStemmeskjemaId sisteStemmeskjemaId;

    @PostConstruct
    public void initialize() {
        LOG.info("GrunnlagsdataCache.initialize() start");
        long start = System.currentTimeMillis();

        String initialdataFraDB = grunnlagsdataService.getTilstand("initialdata").getVerdi();
        String initialdataFraKonfigurasjon = configuration.getInitialdata();
        if (!initialdataFraKonfigurasjon.equals(initialdataFraDB)) {
            throw new ValgnattException("Stopper, mismatch mellom initialdata verdi i configuration (" + initialdataFraKonfigurasjon + ") og i databasen (" + initialdataFraDB + ")");
        }
        grunnlagsdataService.populerPartilister(partilister);
        grunnlagsdataService.initialiserGrunnlagsdatamodell(geografier);
        grunnlagsdataService.initialiserMandaterOgKandidater(this);
        grunnlagsdataService.populerPartierPrStemmekrets(this);
        grunnlagsdataService.populerHistoriskeStemmetallPrParti(this);
        grunnlagsdataService.populerHistoriskeStemmeberettigede(this);
        grunnlagsdataService.lesResultaterFraMandatberegning(this);
        grunnlagsdataService.populerStemmetallPrParti(this);
        grunnlagsdataService.populerOppgjørPrKrets(this);

        LOG.info("GrunnlagsdataCache.initialize() slutt (" + (System.currentTimeMillis() - start) + " ms)");
    }

    public void nullstill() {
        geografier = new TreeMap<>();
        partilister = new TreeMap<>();
    }

    public Partiliste getPartiliste(ValgType valgType, String partikode) {
        return partilister.get(new PartilisteId(valgType, partikode));
    }

    public Land getLand(ValgType valgType) {
        return geografier.get(new ValgTypeAar(valgType, getValgAar()));
    }

    public int getValgAar() {
        return getSisteValgaarIGrunnlagsdata();
    }

    private int getSisteValgaarIGrunnlagsdata() {
        int valgår = 0;
        for (ValgTypeAar valgTypeAar : geografier.keySet()) {
            if (valgTypeAar.getValgAar() > valgår) {
                valgår = valgTypeAar.getValgAar();
            }
        }
        return valgår;
    }

    public Collection<Fylke> getFylker(ValgType valgType) {
        return geografier.get(new ValgTypeAar(valgType, getValgAar())).getFylker().values();
    }

    public Fylke getFylke(ValgType valgType, String fylkeNr) {
        return geografier.get(new ValgTypeAar(valgType, getValgAar())).getFylke(fylkeNr);
    }

    public Valgdistrikt getValgdistrikt(ValgType valgType, String valgdistriktnr) {
        Land land = geografier.get(new ValgTypeAar(valgType, getValgAar()));
        if (valgType == ValgType.SA) {
            return land.getSamevalgdistrikt(valgdistriktnr).getValgdistrikt();
        } else if (valgType == ValgType.KO) {
            return land.getKommune(valgdistriktnr).getValgdistrikt();
        } else { // ST/FY
            if (Konstanter.VALGDISTRIKT_NORGE.equals(valgdistriktnr)) {
                return land.getValgdistrikt();
            } else {
                return land.getFylke(valgdistriktnr).getValgdistrikt();
            }
        }
    }

    public Collection<Valgdistrikt> getStortingsvalgdistrikter() {
        // Henter stortingsvalgdistriker fra årets valg (hvis det er et ST-valg) eller fra forrige
        // valg (dersom det var et ST-valg). Hvis ikke returneres et tomt sett.
        Map<String, Valgdistrikt> valgdistriktMap = new TreeMap<>();
        Land geografiMedSTValg = geografier.get(new ValgTypeAar(ValgType.ST, getValgAar()));
        if (geografiMedSTValg == null) {
            geografiMedSTValg = geografier.get(new ValgTypeAar(ValgType.ST, getValgAar() - 2));
        }
        if (geografiMedSTValg != null) {
            for (Kommune kommune : geografiMedSTValg.getKommuner().values()) {
                valgdistriktMap.put(kommune.getValgdistrikt().getId().getNr(), kommune.getValgdistrikt());
            }
        }
        return valgdistriktMap.values();
    }

    public boolean hasValgType(ValgType valgType) {
        for (ValgType current : getValgTyper()) {
            if (current == valgType) {
                return true;
            }
        }
        return false;
    }

    public ValgType[] getValgTyper() {
        ArrayList<ValgType> valgtyper = new ArrayList<>();
        for (ValgTypeAar valgTypeAar : geografier.keySet()) {
            if (valgTypeAar.getValgAar() == getValgAar()) {
                valgtyper.add(valgTypeAar.getValgType());
            }
        }
        return valgtyper.toArray(new ValgType[valgtyper.size()]);
    }

    public Collection<Kommune> getKommuner(ValgType valgType) {
        return geografier.get(new ValgTypeAar(valgType, getValgAar())).getKommuner().values();
    }

    public Kommune getKommune(ValgType valgType, String kommNr) {
        return geografier.get(new ValgTypeAar(valgType, getValgAar())).getKommune(kommNr);
    }

    public Collection<Stemmekrets> getStemmekretser(ValgType valgType) {
        return geografier.get(new ValgTypeAar(valgType, getValgAar())).getStemmekretser().values();
    }

    public Stemmekrets getStemmekrets(ValgType valgType, String kommnr, String kretsnr) {
        return geografier.get(new ValgTypeAar(valgType, getValgAar())).getKommune(kommnr).getStemmekrets(kretsnr);
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public SisteStemmeskjemaId getSisteStemmeskjemaId() {
        return sisteStemmeskjemaId;
    }

    public void setSisteStemmeskjemaId(SisteStemmeskjemaId sisteStemmeskjemaId) {
        this.sisteStemmeskjemaId = sisteStemmeskjemaId;
    }
}
