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

import java.util.Collections;
import java.util.Objects;

import javax.annotation.Nullable;
import javax.ejb.Stateless;
import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.boundary.ErgebnisDAO;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.AbstractGebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
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.replikation.boundary.ReplikationErzeugen;
import de.ivu.elect.business.stimmeingabe.control.GebietAusbleibendToggle;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingabestatus;
import de.ivu.elect.business.stimmeingabe.entity.ErgebniseingangErfasser;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.entity.WahlParameterLoader;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;

@Stateless
public class ErgebniseingangStatus {

  @Inject
  private transient WahlDAO wahlDAO;

  @Inject
  private transient GebietDAO gebietDAO;

  @Inject
  private transient WahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO;

  @Inject
  private transient ErgebnisDAO ergebnisDAO;

  @Inject
  private transient ReplikationErzeugen replikationErzeugen;

  @Inject
  private transient WahlParameterLoader wahlParameterLoader;

  @Inject
  private transient GebietAusbleibendToggle gebietAusbleibendToggle;

  @Inject
  private transient PraesentationGenerierenAnstossen praesentationGenerierenAnstossen;

  @Inject
  private transient StimmeingabeErfassungLoeschenExportEvent stimmeingabeErfassungLoeschenExportEvent;

  public ErgebniseingangStatus() {
    super();
  }

  public Ergebniseingabestatus setzeGebietErgebniseingabestatus(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebietNotAttached, Ergebniseingabestatus ergebniseingabestatus,
                                                                Ergebnis ergebnisNotAttached) {
    return setzeGebietErgebniseingabestatus(wahldurchfuehrungsgebietNotAttached, ergebniseingabestatus, ergebnisNotAttached, null);

  }

  public Ergebniseingabestatus setzeGebietErgebniseingabestatus(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebietNotAttached, Ergebniseingabestatus ergebniseingabestatus,
                                                                Ergebnis ergebnisNotAttached, @Nullable Gebiet erfassenInGebietNotAttched) {
    Wahldurchfuehrungsgebiet gebiet = attach(wahldurchfuehrungsgebietNotAttached);
    Wahldurchfuehrungsgebiet erfassenInGebiet = attach(erfassenInGebietNotAttched);
    Ergebnis ergebnis = attach(ergebnisNotAttached);

    if (isErgebniseingabestatusSetzenMoeglich(ergebnis, gebiet, ergebniseingabestatus)) {

      setzeGebietErgebniseingabestatusOhnePruefung(ergebniseingabestatus, gebiet, ergebnis.getErgebnisart(), erfassenInGebiet);

      if (ergebniseingabestatus.isFreigegeben()) {
        PraesentationAutomatischenExportAnstossen praesentationAutomatischenExportAnstossen = wahlParameterLoader.resolveEnumValue(ergebnis.getWahl(),
            WahlParameterWerte.PREASENTATIION_AUTOMATISCHEN_EXPORT_ANSTOSSEN, PraesentationAutomatischenExportAnstossen.class);
        if (praesentationAutomatischenExportAnstossen != null && praesentationAutomatischenExportAnstossen.isNachFreigabeAnstossen()) {
          praesentationGenerierenAnstossen.automatischePraesentationGenerierenHintergrungAnstossen(ergebnis);
        }
      }
    }
    return gebiet.getErgebniseingabestatus(ergebnis);
  }

  protected void setzeGebietErgebniseingabestatusOhnePruefung(Ergebniseingabestatus ergebniseingabestatus, Wahldurchfuehrungsgebiet gebiet,
                                                              Ergebnisart ergebnisart, Wahldurchfuehrungsgebiet erfassenInGebiet) {

    if (erfassenInGebiet != null && erfassenInGebiet.equals(gebiet.getErfasstIn())) {
      return; // nichts zu tun
    }

    // Endlosschleife wegen ELECT-8621 verhindern
    if (ergebniseingabestatus.equals(gebiet.getErgebniseingabestatus(ergebnisart)) && erfassenInGebiet == null) {
      return; // nichts zu tun
    }

    if (isChangedErfasstIn(gebiet, erfassenInGebiet)) {
      gebiet.setErfasstIn(erfassenInGebiet);
      stimmeingabeErfassungLoeschenExportEvent.sendEvent(Collections.singletonList(gebiet.getPk()), ErgebniseingangErfasser.benutzerOrDefault().getKennung(),
          Ergebniseingangsart.getErgebniseingangsartFuerErgebnisArt(ergebnisart));
    }

    if (ergebnisart.isVorlaeufig() || ergebniseingabestatus.isAusbleibend() && erfassenInGebiet != null) {
      checkAusbleibendToggle(gebiet, gebiet.getEingabestatusVorlaeufig(), ergebniseingabestatus);
      gebiet.setEingabestatusVorlaeufig(ergebniseingabestatus);
    }

    if (ergebnisart.isEndgueltig() || ergebniseingabestatus.isAusbleibend() && erfassenInGebiet != null) {
      checkAusbleibendToggle(gebiet, gebiet.getEingabestatusEndgueltig(), ergebniseingabestatus);
      gebiet.setEingabestatusEndgueltig(ergebniseingabestatus);
    }


    // ELECT-8315
    for (Gebiet zielGebiet : gebiet.getZielGebieteBeiAufteilung(wahlDAO, gebietDAO)) {
      Wahldurchfuehrungsgebiet erfassenInGebietZielGebiet = null;
      if (erfassenInGebiet != null) {
        Gebiet erfassenZiel = AbstractGebiet.findeGebietBeiAufteilungHandleWithCare(erfassenInGebiet, zielGebiet.getWahl(), gebietDAO);
        if (erfassenZiel != null) {
          erfassenInGebietZielGebiet = erfassenZiel.castTo(Wahldurchfuehrungsgebiet.class).getEntity();
        }
      }
      setzeGebietErgebniseingabestatusOhnePruefung(ergebniseingabestatus, zielGebiet.castTo(Wahldurchfuehrungsgebiet.class).getEntity(), ergebnisart,
          erfassenInGebietZielGebiet);
    }

    replikationErzeugen.replikationGebietUpdate(Collections.singletonList(gebiet));
  }

  private boolean isChangedErfasstIn(Wahldurchfuehrungsgebiet gebiet, Wahldurchfuehrungsgebiet erfassenInGebiet) {
    return !Objects.equals(gebiet.getErfasstIn(), erfassenInGebiet);
  }

  private void checkAusbleibendToggle(Wahldurchfuehrungsgebiet gebiet, Ergebniseingabestatus alt, Ergebniseingabestatus neu) {
    if (alt.isAusbleibend()) {
      if (!neu.isAusbleibend()) {
        gebietAusbleibendToggle.setzeGebietNichtMehrAusbleibend(gebiet);
      }
    } else if (neu.isAusbleibend()) {
      gebietAusbleibendToggle.setzeGebietAusbleibend(gebiet);
    }
  }

  protected boolean isErgebniseingabestatusSetzenMoeglich(Ergebnis ergebnis, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingabestatus ergebniseingabestatus) {
    return !ergebniseingabestatus.equals(wahldurchfuehrungsgebiet.getErgebniseingabestatus(ergebnis)) && (!ergebniseingabestatus.isAusbleibend() || isAusbleibendMoeglich(wahldurchfuehrungsgebiet));
  }

  public boolean isAusbleibendMoeglich(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return wahldurchfuehrungsgebietDAO.findByErfasstFuerGebiete(wahldurchfuehrungsgebiet).isEmpty();
  }

  protected Ergebnis attach(Ergebnis template) {
    return ergebnisDAO.attach(template);
  }

  protected Wahldurchfuehrungsgebiet attach(Wahldurchfuehrungsgebiet template) {
    return wahldurchfuehrungsgebietDAO.attach(template);
  }

  @Nullable
  protected Wahldurchfuehrungsgebiet attach(Gebiet template) {
    if (template==null) {
      return null;
    }
    return wahldurchfuehrungsgebietDAO.findByPrimaryKey(template.getPk());
  }
}
