package no.valg.valgnatt.backend.mandatberegning;

import no.valg.valgnatt.backend.mandatberegning.beregning.DistriktsmandatResultat;
import no.valg.valgnatt.backend.mandatberegning.beregning.UtjevningsmandatResultat;
import no.valg.valgnatt.database.Konstanter;
import no.valg.valgnatt.database.dao.MandatberegningDao;
import no.valg.valgnatt.database.geografi.Fylke;
import no.valg.valgnatt.database.geografi.Land;
import no.valg.valgnatt.database.geografi.Område;
import no.valg.valgnatt.database.geografi.PartiMandater;
import no.valg.valgnatt.database.hibernate.Mandatberegning;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;

@Transactional
@Service
public class MandatBeregningService {

	@Resource
	private MandatberegningDao mandatberegningDao;

	public void lagreDistriktsmandatberegning(Land land, boolean prognose) {
		Collection<Mandatberegning> mandatberegningerLandet = mandatberegningDao.find(land.getValgType(), Konstanter.VALGDISTRIKT_NORGE, true, prognose);
		Map<String, Integer> stemmerPrParti = land.getValgdistrikt().getStemmerPrPartiTilMandatberegning(false, false, prognose);
		for (Mandatberegning rad : mandatberegningerLandet) {
			String partikode = rad.getId().getPartikode();
			rad.setStemmer(stemmerPrParti.get(partikode));
			int distriktsmandater = 0;
			int distriktsmandaterGittSTValg = 0;
			for (Fylke fylke : land.getFylker().values()) {
				PartiMandater partiMandaterIFylket = fylke.sumMandaterParti(partikode);
				if (partiMandaterIFylket != null) {
					distriktsmandater += partiMandaterIFylket.finnBesteMandatData(prognose).getDistriktsmandater().getAntall();
					distriktsmandaterGittSTValg += partiMandaterIFylket.finnBesteMandatData(prognose).getAntallDistriktsmandaterGittSTValg();
				}
			}
			land.getValgdistrikt().getPartiMandater(partikode).getMandatData(prognose).getDistriktsmandater().setAntall(distriktsmandater);
			rad.setAntall(distriktsmandater);
			land.getValgdistrikt().getPartiMandater(partikode).getMandatData(prognose).setAntallDistriktsmandaterGittSTValg(distriktsmandaterGittSTValg);
			rad.setAntallSTDMiFY(distriktsmandaterGittSTValg);
			rad.getId().setPrognose(prognose);
			mandatberegningDao.merge(rad);
		}
	}

	public void lagreUtjevningsmandatberegning(Land land, UtjevningsmandatResultat resultater, boolean prognose) {
		Collection<Mandatberegning> mandatberegningerLandet = mandatberegningDao.find(land.getValgType(), Konstanter.VALGDISTRIKT_NORGE, true, prognose);
		for (Mandatberegning rad : mandatberegningerLandet) {
			String partikode = rad.getId().getPartikode();
			Map<String, Integer> utjMandaterPrParti = null;
			if (resultater != null && resultater.getPartiPrFylke() != null) {
				utjMandaterPrParti = resultater.getPartiPrFylke().values().stream().collect(Collectors.groupingBy(parti -> parti, Collectors.summingInt(parti -> 1)));
			}
			rad.setAntallUtj(utjMandaterPrParti == null ? null : utjMandaterPrParti.get(partikode));
			mandatberegningDao.merge(rad);
		}

		Collection<Mandatberegning> mandatberegningerFylker = mandatberegningDao.find(land.getValgType(), Konstanter.VALGDISTRIKT_NORGE, false, prognose);
		for (Mandatberegning rad : mandatberegningerFylker) {
			String fylkeNr = rad.getId().getValgdistriktnr();
			String partikode = rad.getId().getPartikode();
			rad.setAntallUtj(resultater.getPartiPrFylke().get(fylkeNr).equals(partikode) ? 1 : 0);
			rad.setMandatrangUtj(resultater.getMandatnrPrPartiPrFylke().get(fylkeNr).getOrDefault(partikode, 0));
			rad.setRestkvotientUtj(BigDecimal.ZERO);
			if (resultater.getKvotienterPrPartiPrFylke().get(fylkeNr).containsKey(partikode)) {
				rad.setRestkvotientUtj(resultater.getKvotienterPrPartiPrFylke().get(fylkeNr).get(partikode).getKvotient());
			}
			mandatberegningDao.merge(rad);
		}
	}

	public void lagreDistriktsmandatberegning(Område område, DistriktsmandatResultat distriktsmandatResultat, Map<String, Integer> antallSTDMiFYMap, boolean prognose) {
		Collection<Mandatberegning> mandatberegningRader = mandatberegningDao.find(område.getValgType(), område.getValgdistrikt().getId().getNr(), prognose);
		for (Mandatberegning rad : mandatberegningRader) {
			String partikode = rad.getId().getPartikode();
			if (distriktsmandatResultat != null) { // Dersom det ikke finnes noen stemmer, så bruker vi de initialiserte data (og skriver over evt. gamle utdaterte data)
				rad.setAntall(distriktsmandatResultat.getAntallPrParti().get(partikode));
				rad.setAntallSTDMiFY(antallSTDMiFYMap == null ? null : antallSTDMiFYMap.get(partikode));
				rad.getId().setPrognose(prognose);
				rad.setSisteKvotient(distriktsmandatResultat.getSisteKvotientPrParti().getOrDefault(partikode, BigDecimal.ZERO));
				rad.setSisteRang(distriktsmandatResultat.getLavesteMandatrangPrParti().getOrDefault(partikode, 0));
				rad.setNesteKvotient(distriktsmandatResultat.getNesteKvotientPrParti().getOrDefault(partikode, BigDecimal.ZERO));
				rad.setNesteRang(distriktsmandatResultat.getHøyesteMandatrangPrParti().getOrDefault(partikode, 0));
			}
			mandatberegningDao.merge(rad);
		}
	}

	public void lagreStemmetallBruktIBeregningen(Område område, boolean prognose) {
		Collection<Mandatberegning> mandatberegningRader = mandatberegningDao.find(område.getValgType(), område.getValgdistrikt().getId().getNr(), prognose);
		Map<String, Integer> stemmerPrParti = område.getValgdistrikt().getStemmerPrPartiTilMandatberegning(false, false, prognose);
		for (Mandatberegning rad : mandatberegningRader) {
			rad.setStemmer(stemmerPrParti.get(rad.getId().getPartikode()));
			mandatberegningDao.merge(rad);
		}
	}
}

