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

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

import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietDAO;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietHierarchyBuilder;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietCache;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebietTK;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.jee.faces.Messages;


import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static de.ivu.elect.business.wahlvorschlag.control.DirektkandidatCreate.bestimmeMoeglicheRelativePosition;

public class ListeCreate extends AbstractListeCreate {

  @Inject
  protected transient GruppeImGebietDAO gruppeImGebietDao;

  @Inject
  protected transient GruppeImGebietHierarchyBuilder gruppeImGebietHierarchyBuilder;

  @Inject
  protected transient UniqueWahlvorschlagUkValidator uniqueWahlvorschlagUkValidator;

  @Inject
  private transient Messages messages;

  public ListeCreate() {
    super();
  }

  @Override
  public void save(Liste liste) {
    saveAndUpdateGruppeImGebiet(liste.getGruppeImGebiet(), liste);
  }

  public void save(Collection<Liste> listen) {
    Map<GruppeImGebietTK, GruppeImGebiet> bereitsPersistenteGruppenImGebiet = new HashMap<>();
    for (Liste liste : listen) {
      saveAndUpdateGruppeImGebiet(liste.getGruppeImGebiet(), liste, bereitsPersistenteGruppenImGebiet);
    }
  }

  public void saveAndUpdateGruppeImGebiet(GruppeImGebiet gruppeImGebiet, Liste liste) {
    saveAndUpdateGruppeImGebiet(gruppeImGebiet, liste, new HashMap<>());
  }

  private void saveAndUpdateGruppeImGebiet(GruppeImGebiet gruppeImGebiet, Liste liste,
      Map<GruppeImGebietTK, GruppeImGebiet> bereitsPersistenteGruppenImGebiet) {
    // NOTE that the GebietUK is part of the WahlvorschlagUK and must be set before validating the unique WahlvorschlagUK!
    updateGruppeImGebiet(gruppeImGebiet, liste, bereitsPersistenteGruppenImGebiet);

    assertUniqueWahlvorschlagUk(liste);

    super.save(liste);
  }

  private void updateGruppeImGebiet(GruppeImGebiet template, Liste liste,
      Map<GruppeImGebietTK, GruppeImGebiet> bereitsPersistenteGruppenImGebiet) {
    GruppeImGebiet gruppeImGebiet = template;

    // WASDE-635
    gruppeImGebietHierarchyBuilder.updateGruppeImGebietNamen(gruppeImGebiet, liste);

    if (gruppeImGebiet.isRelativePositionGesetzt()) {
      gruppeImGebiet.setVorgegebenePosition(gruppeImGebiet.getRelativePosition());
    }

    try {
      gruppeImGebiet = gruppeImGebietDao.findByGruppeAndGebiet(template.getGruppe(), template.getGebiet());

      template.copyEntityAttributesUsingSetter(gruppeImGebiet);
    } catch (NoResultException ignored) {
      // Do nothing, but save a new entity.
    }

    liste.setGruppeImGebiet(gruppeImGebiet);

    bestimmeUndSetzeRelativePosition(gruppeImGebiet);

    List<GruppeImGebiet> gruppenInGebieten = gruppeImGebietHierarchyBuilder.saveAndCompleteHierarchy(liste.getGruppeImGebiet(), bereitsPersistenteGruppenImGebiet);

    if (!gruppeImGebiet.isPersistent()) {
      // Kann sein, dass der GruppeImGebiet-Finder durch den Hierarchy-Builder neu erzeugte GruppeImGebiet noch nicht findet. (ELECT-15799)
      GruppeImGebietTK gruppeImGebietTK = gruppeImGebiet.getGruppeImGebietTK();
      liste.setGruppeImGebiet(gruppenInGebieten.stream().filter(gig -> gig.getGruppeImGebietTK().equals(gruppeImGebietTK)).findFirst().orElse(gruppeImGebiet));
    }


  }

  protected void bestimmeUndSetzeRelativePosition(GruppeImGebiet gruppeImGebiet) {
    if (gruppeImGebiet.getRelativePosition() == null) {
      gruppeImGebiet.setRelativePosition(bestimmeMoeglicheRelativePosition(gruppeImGebiet, gruppeImGebietDao));
    }
  }

  public void save(GruppeImGebiet gruppeImGebiet, Liste liste, GruppeImGebietCache gruppeImGebietCache) {
    // NOTE that the GebietUK is part of the WahlvorschlagUK and must be set before validating the unique WahlvorschlagUK!
    liste.setGruppeImGebiet(gruppeImGebietCache.findOrCreate(gruppeImGebiet));

    assertUniqueWahlvorschlagUk(liste);

    super.save(liste);
  }

  private void assertUniqueWahlvorschlagUk(Liste entity) {
    if (!isUniqueWahlvorschlagUk(entity)) {
      throw new NonUniqueWahlvorschlagUkException(entity.getWahlvorschlagUK(), messages);
    }
  }

  protected boolean isUniqueWahlvorschlagUk(Liste entity) {
    return uniqueWahlvorschlagUkValidator.isUniqueWahlvorschlagUk(entity);
  }
}
