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

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.ejb.Stateless;
import javax.enterprise.inject.Specializes;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.gebietsbaum.control.AWerteAktualisierer;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietUebernehmenContext;
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 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 WasGebietEditHelper wasGebietEditHelper;

  @Inject
  private transient LetzterEingangCache letzterEingangCache;

  public WasGebietEdit() {
    super();
  }

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

    AWerteAktualisierer aWerteAktualisierer = new AWerteAktualisierer(
        wahldurchfuehrungsgebiet.getWahl(),
        wahldurchfuehrungsgebietExtendedDAO,
        wasGebietEditHelper.getUmgangHinterlegteAWerte(wahldurchfuehrungsgebiet.getWahl()));

    aWerteAktualisieren(wahldurchfuehrungsgebiet, aWerteAktualisierer);

    super.save(entity);

    Set<Gebiet> zielGebieteBeiAufteilung = entity.getZielGebieteBeiAufteilung(wahlExtendedDAODAO, extendedDao);
    for (Gebiet zielGebiet : zielGebieteBeiAufteilung) {
      Wahldurchfuehrungsgebiet zielWahldurchfuehrungsgebiet = wahldurchfuehrungsgebietExtendedDAO.findByPrimaryKey(zielGebiet.getPk());

      GebietUebernehmenContext uebernehmenContext = new GebietUebernehmenContext(false /* nicht benötigt in ziel */, true, false /* lieber mal nicht */,
          true/*legacy*/, false/* ELECTKOM-631 A-Werte nicht mehr übernehmen */);
      gebietUebernehmen.kopierereGebietUnveraendert(wahldurchfuehrungsgebiet, zielWahldurchfuehrungsgebiet, uebernehmenContext);

      //      aWerteAktualisieren(zielWahldurchfuehrungsgebiet, aWerteAktualisierer); /* ELECTKOM-631 A-Werte nicht mehr übernehmen*/

    }
    if (!zielGebieteBeiAufteilung.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) {
    Gebiet gebiet = dao.attach(entity);

    AWerteAktualisierer aWerteAktualisierer = new AWerteAktualisierer(gebiet.getWahl(), wahldurchfuehrungsgebietDAO,
        wasGebietEditHelper.getUmgangHinterlegteAWerte(gebiet.getWahl()));

    aWerteZuruecksetzen(gebiet, aWerteAktualisierer);

    for (Gebiet 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) {
    super.postSpeichereReichenfolge(wahltermin, wahl);

    // ELECT-13038
    wahldurchfuehrungsgebietDAO.flush();
    AWerteAktualisierer aWerteAktualisierer = new AWerteAktualisierer(
        wahl,
        wahldurchfuehrungsgebietDAO,
        wasGebietEditHelper.getUmgangHinterlegteAWerte(wahl));
    Set<Gebiet> betrachtet = new HashSet<>();
    for (Wahldurchfuehrungsgebiet 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
  }

  private void aWerteZuruecksetzen(Gebiet gebiet, AWerteAktualisierer aWerteAktualisierer) {
    Wahldurchfuehrungsgebiet 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 (Gebiet zielGebiet : gebiet.getZielGebieteBeiAufteilung(wahlDAO, dao)) {
      zielGebiet.setPosition(neuePosition);
    }
  }
}
