
package de.ivu.elect.business.gebietsbaum.boundary;

import de.ivu.elect.business.gebietsbaum.control.AWerteAktualisierer;
import de.ivu.elect.business.gebietsbaum.control.AWerteAktualisiererExtendedFactory;
import de.ivu.elect.business.gebietsbaum.control.AWerteAktualisiererFactory;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.replikation.boundary.ReplikationErzeugen;
import de.ivu.elect.business.stimmeingabe.control.LetzterEingangCache;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.boundary.WahlExtendedDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ejb.Stateless;
import javax.enterprise.inject.Specializes;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;

@Stateless
@Specializes
public class WasGebietEdit extends CoreGebietEdit {

  private static final Logger LOG = LoggerFactory.getLogger(WasGebietEdit.class);

  @Inject
  private transient WahlDAO wahlDAO;

  @Inject
  private transient WahlExtendedDAO wahlExtendedDAODAO;

  @Inject
  private transient WahldurchfuehrungsgebietExtendedDAO wahldurchfuehrungsgebietExtendedDAO;

  @Inject
  private transient WahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO;

  @Inject
  private transient ReplikationErzeugen replikationErzeugen;

  @Inject
  private transient GebietUebernehmen gebietUebernehmen;

  @Inject
  private transient LetzterEingangCache letzterEingangCache;

  @Inject
  private transient AWerteAktualisiererExtendedFactory aWerteAktualisiererExtendedFactory;

  @Inject
  private transient AWerteAktualisiererFactory aWerteAktualisiererFactory;

  public WasGebietEdit() {
    super();
  }

  @Override
  public void save(Gebiet entity) {
    var wahldurchfuehrungsgebiet = castTo(entity, Wahldurchfuehrungsgebiet.class);

    var aWerteAktualisierer = aWerteAktualisiererExtendedFactory.createAWerteAktualisierer(wahldurchfuehrungsgebiet.getWahl());

    aWerteAktualisieren(wahldurchfuehrungsgebiet, aWerteAktualisierer);

    super.save(entity);

    if (!entity.aktualisiereBeiGebietsaufteilung(gebietUebernehmen, extendedDao, wahlExtendedDAODAO).isEmpty()) {
      flushToSave();
    }

    if (replikationErzeugen.isReplikationNotwendig()) {
      List<Wahldurchfuehrungsgebiet> zuReplizieren = new ArrayList<>();

      Gebiet gebiet = wahldurchfuehrungsgebiet;

      while (gebiet != null) {
        zuReplizieren.add(wahldurchfuehrungsgebietExtendedDAO.refresh(wahldurchfuehrungsgebietExtendedDAO.findByPrimaryKey(gebiet.getId())));
        gebiet = gebiet.getUebergeordnetesGebiet();
      }

      replikationErzeugen.replikationGebietUpdate(zuReplizieren);
    }
  }


  @Override
  public void delete(Gebiet entity) {
    var gebiet = dao.attach(entity);

    var aWerteAktualisierer = aWerteAktualisiererFactory.createAWerteAktualisierer(gebiet.getWahl());

    aWerteZuruecksetzen(gebiet, aWerteAktualisierer);

    for (var zielGebiet : gebiet.getZielGebieteBeiAufteilung(wahlDAO, dao)) {
      aWerteZuruecksetzen(zielGebiet, aWerteAktualisierer);
      LOG.info("Lösche Gebiet: " + zielGebiet + " in Wahl: " + zielGebiet.getWahl());
      super.delete(zielGebiet);
    }

    super.delete(entity);
  }

  @Override
  protected void postSpeichereReichenfolge(Wahltermin wahltermin, Wahl wahl, Set<Gebiet> gebieteMitNeuerHierarchie) {
    super.postSpeichereReichenfolge(wahltermin, wahl, gebieteMitNeuerHierarchie);

    // ELECT-13038
    wahldurchfuehrungsgebietDAO.flush();
    var aWerteAktualisierer = aWerteAktualisiererFactory.createAWerteAktualisierer(wahl);
    Set<Gebiet> betrachtet = new HashSet<>();
    for (var wahldurchfuehrungsgebiet : wahldurchfuehrungsgebietDAO.findByWahl(wahl)) {
      if (wahldurchfuehrungsgebiet.isErfassungsgebietLautGebietsart()) {
        if (wahldurchfuehrungsgebiet.getUebergeordnetesGebiet() != null) {
          if (betrachtet.add(wahldurchfuehrungsgebiet.getUebergeordnetesGebiet())) {
            aWerteAktualisierer.aktualisiereAWerteFuerUebergeordneteGebieteVon(wahldurchfuehrungsgebiet);
          }
        }
      }
    }
    letzterEingangCache.resetCache(); // Passt ja nicht mehr

    // ELECT-15877
    for (var gebiet : gebieteMitNeuerHierarchie) {

      var uebergeordnetesGebiet = gebiet.getUebergeordnetesGebiet();
      if (uebergeordnetesGebiet != null) {
        for (var zielGebiet : gebiet.getZielGebieteBeiAufteilung(wahlDAO, dao)) {
          uebergeordnetesGebiet.getZielGebietBeiAufteilungFuerBestimmteWahl(zielGebiet.getWahl(), dao).ifPresent(zielGebiet::setUebergeordnetesGebiet);
        }
      }
    }

  }

  private void aWerteZuruecksetzen(Gebiet gebiet, AWerteAktualisierer aWerteAktualisierer) {
    var wahldurchfuehrungsgebiet = castTo(gebiet, Wahldurchfuehrungsgebiet.class);
    aWerteAktualisierer.aWerteZuruecksetzen(wahldurchfuehrungsgebiet);
    aWerteAktualisieren(wahldurchfuehrungsgebiet, aWerteAktualisierer);
  }

  private void aWerteAktualisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, AWerteAktualisierer aWerteAktualisierer) {
    if (aWerteAktualisierer.isAWerteAktualisieren(wahldurchfuehrungsgebiet)) {
      aWerteAktualisierer.aktualisiereAWerteFuerUebergeordneteGebieteVon(wahldurchfuehrungsgebiet);
    }
  }

  @Override
  protected void setzeNeuePosition(Gebiet gebiet, Long neuePosition) {
    super.setzeNeuePosition(gebiet, neuePosition);
    for (var zielGebiet : gebiet.getZielGebieteBeiAufteilung(wahlDAO, dao)) {
      zielGebiet.setPosition(neuePosition);
    }
  }
}
