
package de.ivu.elect.business.wahlbeteiligung.control;

import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.internet.boundary.PraesentationGenerierenAnstossen;
import de.ivu.elect.business.praesentation.entity.PraesentationAutomatischenExportAnstossen;
import de.ivu.elect.business.wahl.entity.WahlParameterLoader;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungAuswertungDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungDAO;
import de.ivu.elect.business.wahlbeteiligung.entity.Wahlbeteiligung;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungAuswertung;

import javax.inject.Inject;
import java.util.*;

import static java.util.Objects.isNull;

public class WahlbeteiligungAktualisierer {

  @Inject
  private transient WahlbeteiligungDAO dao;

  @Inject
  private transient WahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO;

  @Inject
  private transient WahlbeteiligungAuswertungDAO wahlbeteiligungAuswertungDAO;

  @Inject
  private transient WahlbeteiligungCreate wahlbeteiligungCreate;

  @Inject
  private transient WahlParameterLoader wahlParameterLoader;

  @Inject
  private transient PraesentationGenerierenAnstossen praesentationGenerierenAnstossen;

  public void aktualisiereWahlbeteiligungen(String erfasser, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebietNotAttached,
                                            Map<WahlbeteiligungAuswertung, Wahlbeteiligung> wahlbeteiligungProAuswertung,
                                            Ergebnis ergebnisNotAttched) {

    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebietInDb = wahldurchfuehrungsgebietDAO.attach(wahldurchfuehrungsgebietNotAttached);

    List<Wahlbeteiligung> wahlbeteiligungenInDb = new ArrayList<>(dao.findByWahldurchfuehrungsgebiet(wahldurchfuehrungsgebietInDb));

    Map<WahlbeteiligungAuswertung, Wahlbeteiligung> wahlbeteiligungProAuswertungInDb = new HashMap<>();

    for (Wahlbeteiligung wahlbeteiligungInDb : wahlbeteiligungenInDb) {
      wahlbeteiligungProAuswertungInDb.put(wahlbeteiligungInDb.getWahlbeteiligungAuswertung(), wahlbeteiligungInDb);
    }

    List<Wahlbeteiligung> zuLoeschendeBeteiligungen = new ArrayList<>();
    List<Wahlbeteiligung> zuAktualisierendeBeteiligungen = new ArrayList<>();
    List<Wahlbeteiligung> zuErstellendeBeteiligungen = new ArrayList<>();

    for (Map.Entry<WahlbeteiligungAuswertung, Wahlbeteiligung> entry : wahlbeteiligungProAuswertung.entrySet()) {
      Wahlbeteiligung wahlbeteiligung = entry.getValue();

      if (isNull(wahlbeteiligung.getAnzahlWaehlerGesamt())) {

        // Lösche die Wahlbeteiligung, falls der Wert leer gelassen wurde.

        if (wahlbeteiligungProAuswertungInDb.containsKey(entry.getKey())) {
          zuLoeschendeBeteiligungen.add(wahlbeteiligung);
        }
      } else {

        // Update die Wahlbeteiligung, falls eine Erfassung schon vorliegt, sonst lege ein neues Objekt an.

        if (wahlbeteiligungProAuswertungInDb.containsKey(entry.getKey())) {
          Wahlbeteiligung wahlbeteiligungInDb = wahlbeteiligungProAuswertungInDb.get(entry.getKey());

          if (!wahlbeteiligung.getAnzahlWaehlerGesamt().equals(wahlbeteiligungInDb.getAnzahlWaehlerGesamt())) {
            zuAktualisierendeBeteiligungen.add(wahlbeteiligung);
          }
        } else {
          zuErstellendeBeteiligungen.add(wahlbeteiligung);
        }
      }
    }

    boolean werteUnveraendert = zuLoeschendeBeteiligungen.isEmpty() && zuAktualisierendeBeteiligungen.isEmpty() && zuErstellendeBeteiligungen.isEmpty();

    Date zeitstempel = new Date();

    loescheBeteiligungen(wahlbeteiligungProAuswertungInDb, zuLoeschendeBeteiligungen);
    aktualisierendeBeteiligungen(erfasser, zeitstempel, wahlbeteiligungProAuswertungInDb, zuAktualisierendeBeteiligungen);
    erstelleBeteiligungen(erfasser, zeitstempel, wahldurchfuehrungsgebietInDb, zuErstellendeBeteiligungen);

    // ELECTBB-352
    if (!werteUnveraendert) {
      PraesentationAutomatischenExportAnstossen praesentationAutomatischenExportAnstossen = wahlParameterLoader
              .resolveEnumValue(wahldurchfuehrungsgebietInDb.getWahl(), WahlParameterWerte.PREASENTATIION_AUTOMATISCHEN_EXPORT_ANSTOSSEN,
                      PraesentationAutomatischenExportAnstossen.class);
      if (praesentationAutomatischenExportAnstossen != null && praesentationAutomatischenExportAnstossen.isNachWahlbeteiligungErfassen()) {
        praesentationGenerierenAnstossen.automatischePraesentationGenerierenHintergrungAnstossen(ergebnisNotAttched);
      }

    }
  }

  private void erstelleBeteiligungen(String erfasser, Date zeitstempel,
      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebietInDb, List<Wahlbeteiligung> zuErstellendeBeteiligungen) {

    for (Wahlbeteiligung wahlbeteiligung : zuErstellendeBeteiligungen) {
      Wahlbeteiligung wahlbeteiligungNeu = new Wahlbeteiligung();
      wahlbeteiligungNeu.setWahldurchfuehrungsgebiet(wahldurchfuehrungsgebietInDb);
      wahlbeteiligungNeu.setWahlbeteiligungAuswertung(
          wahlbeteiligungAuswertungDAO.attach(wahlbeteiligung.getWahlbeteiligungAuswertung()));
      wahlbeteiligungNeu.setAnzahlWaehlerGesamt(wahlbeteiligung.getAnzahlWaehlerGesamt());
      wahlbeteiligungNeu.setAnzahlWaehlerMitWahlschein(wahlbeteiligung.getAnzahlWaehlerMitWahlschein());
      wahlbeteiligungNeu.setErfasser(erfasser);
      wahlbeteiligungNeu.setZeitstempel(zeitstempel);

      wahlbeteiligungCreate.save(wahlbeteiligungNeu);
    }
  }

  private void aktualisierendeBeteiligungen(String erfasser, Date zeitstempel,
      Map<WahlbeteiligungAuswertung, Wahlbeteiligung> wahlbeteiligungProAuswertungInDb,
      List<Wahlbeteiligung> zuAktualisierendeBeteiligungen) {
    for (Wahlbeteiligung wahlbeteiligung : zuAktualisierendeBeteiligungen) {
      Wahlbeteiligung wahlbeteiligungInDb = wahlbeteiligungProAuswertungInDb
          .get(wahlbeteiligung.getWahlbeteiligungAuswertung());
      wahlbeteiligungInDb.setAnzahlWaehlerGesamt(wahlbeteiligung.getAnzahlWaehlerGesamt());
    }
  }

  private void loescheBeteiligungen(Map<WahlbeteiligungAuswertung, Wahlbeteiligung> wahlbeteiligungProAuswertungInDb,
      List<Wahlbeteiligung> zuLoeschendeBeteiligungen) {
    for (Wahlbeteiligung wahlbeteiligung : zuLoeschendeBeteiligungen) {
      dao.remove(wahlbeteiligungProAuswertungInDb.get(wahlbeteiligung.getWahlbeteiligungAuswertung()));
    }
  }
}
