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

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.persistence.NoResultException;

import de.ivu.elect.business.benutzerverwaltung.boundary.BenutzerEinsatzortDAO;
import de.ivu.elect.business.benutzerverwaltung.entity.BenutzerEinsatzort;
import de.ivu.elect.business.kandidatur.boundary.KandidaturDAO;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.staat.boundary.StaatDAO;
import de.ivu.elect.business.staat.entity.Staat;
import de.ivu.elect.business.unterstuetzer.entity.EintragungslisteEintrag;
import de.ivu.elect.business.wahlvorschlag.boundary.WahlvorschlagDAO;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlagsperson.entity.WahlvorschlagsPerson;
import de.ivu.elect.business.wahlvorschlagsperson.entity.WahlvorschlagsPersonTyp;

public class WahlvorschlagsPersonCreate extends AbstractWahlvorschlagsPersonCreate {

  private static final Long DEFAULT_EINGANGSSEQUENZNUMMER = 0L;

  @Inject
  protected transient BenutzerEinsatzortDAO benutzerEinsatzortDAO;

  @Inject
  protected transient StaatDAO staatDAO;

  @Inject
  protected transient WahlvorschlagDAO wahlvorschlagDAO;

  @Inject
  protected transient KandidaturDAO kandidaturDAO;

  public WahlvorschlagsPersonCreate() {
    super();
  }

  @Override
  public void save(WahlvorschlagsPerson wahlvorschlagsPerson) {
    save(wahlvorschlagsPerson, null);
  }

  public void save(WahlvorschlagsPerson wahlvorschlagsPerson, @Nullable EintragungslisteEintrag eintragungslisteEintragDetached) {
    saveIncludingPersonenPruefung(wahlvorschlagsPerson, eintragungslisteEintragDetached);
  }

  public void saveIncludingPersonenPruefung(WahlvorschlagsPerson wahlvorschlagsPerson, @Nullable EintragungslisteEintrag eintragungslisteEintragDetached) {
    save(wahlvorschlagsPerson, wahlvorschlagsPerson.getWahlvorschlag(), wahlvorschlagsPerson.getKandidatur(), eintragungslisteEintragDetached);
  }

  public void saveExcludingPersonenPruefung(WahlvorschlagsPerson wahlvorschlagsPerson) {
    saveExcludingPersonenPruefung(wahlvorschlagsPerson, null);
  }

  public void saveExcludingPersonenPruefung(WahlvorschlagsPerson wahlvorschlagsPerson, EintragungslisteEintrag eintragungslisteEintragDetached) {
    save(wahlvorschlagsPerson, wahlvorschlagsPerson.getWahlvorschlag(), wahlvorschlagsPerson.getKandidatur(), eintragungslisteEintragDetached);
  }

  protected void save(WahlvorschlagsPerson wahlvorschlagsPerson, Wahlvorschlag wahlvorschlagDetached, Kandidatur kandidaturDetached, @Nullable EintragungslisteEintrag eintragungslisteEintragDetached) {
    Wahlvorschlag wahlvorschlag = attach(wahlvorschlagDetached);
    Kandidatur kandidatur = attach(kandidaturDetached);

    wahlvorschlagsPerson.setWahlvorschlag(wahlvorschlag);
    wahlvorschlagsPerson.setKandidatur(kandidatur);
    wahlvorschlagsPerson.setWahl(wahlvorschlag.getWahl());

    // ELECT-8970
    if (wahlvorschlagsPerson.getErstellungZeitpunkt() == null) {
      wahlvorschlagsPerson.setErstellungZeitpunkt(new Date());
    }

    wahlvorschlagsPerson.setErstellungOrt(attach(wahlvorschlagsPerson.getErstellungOrt())); // ELECT-8985

    if (!wahlvorschlagsPerson.hasEingangssequenznummer()) {
      wahlvorschlagsPerson.setEingangssequenznummer(calculateNextEingangssequenznummer(wahlvorschlag, wahlvorschlagsPerson.getWahlvorschlagsPersonTyp()));
    }

    List<Staat> staatsangehoerigkeiten = new ArrayList<>(wahlvorschlagsPerson.getStaatsangehoerigkeitenSorted());

    wahlvorschlagsPerson.setStaatsangehoerigkeitenSorted(attachAll(staatsangehoerigkeiten));

    // NOTE, dass der EintragungslisteEintrag (, falls gesetzt und nicht null) explizit nicht 'attached' wird, da ansonsten die
    // aktualisierten Attribute mit den Werten aus der DB überschrieben werden würden!
    wahlvorschlagsPerson.setEintragungslisteEintrag(eintragungslisteEintragDetached);

    super.save(wahlvorschlagsPerson);
  }

  public Long calculateNextEingangssequenznummer(Wahlvorschlag wahlvorschlag, WahlvorschlagsPersonTyp wahlvorschlagsPersonTyp) {
    Long result = null;

    try {
      result = dao.findByMaxEingangssequenznummerByWahlvorschlagAndTyp(wahlvorschlag, wahlvorschlagsPersonTyp);
    } catch (NoResultException e) {
      result = null;
    }

    if (result == null) {
      result = DEFAULT_EINGANGSSEQUENZNUMMER;
    }

    result++;

    return result;
  }

  private BenutzerEinsatzort attach(BenutzerEinsatzort benutzerEinsatzort) {
    return benutzerEinsatzortDAO.attach(benutzerEinsatzort);
  }

  private Wahlvorschlag attach(Wahlvorschlag wahlvorschlag) {
    return wahlvorschlagDAO.attach(wahlvorschlag);
  }

  private Kandidatur attach(Kandidatur kandidatur) {
    return kandidaturDAO.attach(kandidatur);
  }

  private List<Staat> attachAll(List<Staat> staatsangehoerigkeiten) {
    return staatsangehoerigkeiten.stream().map(staatsangehoerigkeit -> staatDAO.attach(staatsangehoerigkeit)).collect(Collectors.toList());
  }
}
