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

import static java.util.Arrays.asList;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

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

import de.ivu.elect.business.person.entity.PersonStatus;
import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.business.person.entity.PersonenregisterAbgleichStatus;
import de.ivu.elect.business.personenpruefung.control.PersonenKonfliktUebernehmen;
import de.ivu.elect.business.personenpruefung.entity.PersonenKonflikt;
import de.ivu.elect.business.personenpruefung.entity.PersonenKonfliktArt;
import de.ivu.elect.business.personenpruefung.entity.PersonenKonfliktStatus;
import de.ivu.elect.business.wahl.entity.Wahl;

@Stateless
public class PersonenKonfliktEdit extends AbstractPersonenKonfliktEdit {

  @Inject
  private transient PersonenKonfliktUebernehmen personenKonfliktUebernehmen;

  @Inject
  private transient PersonenPruefungStarter personenPruefungStarter;

  public PersonenKonfliktEdit() {
    super();
  }

  @Override
  public void save(PersonenKonflikt konflikt) {
    super.save(konflikt);

    personenKonfliktUebernehmen.uebernehmeDaten(konflikt);
  }

  public void saveAll(Collection<PersonenKonflikt> konflikte) {
    for (PersonenKonflikt konflikt : konflikte) {
      save(konflikt);
    }
  }

  public void resetPersonenKonfliktPersonenregisterAbgleichManuellZuPruefen() {
    resetPersonenKonflikt(PersonenKonfliktArt.ABGLEICH_FEHLGESCHLAGEN, PersonenKonfliktStatus.MANUELL_ZU_PRUEFEN, null);
  }

  public void resetPersonenKonfliktWertelistenAbgleichManuellZuPruefen(List< ? extends Personendaten> betroffenePersonen) {
    resetPersonenKonflikt(PersonenKonfliktArt.WERTELISTEN_ABGLEICH_FEHLGESCHLAGEN, PersonenKonfliktStatus.MANUELL_ZU_PRUEFEN, betroffenePersonen);
  }

  public void resetPersonenKonfliktWertelistenAbgleichManuellZuPruefen(Wahl wahl, List< ? extends Personendaten> betroffenePersonen) {
    resetPersonenKonflikt(wahl, PersonenKonfliktArt.WERTELISTEN_ABGLEICH_FEHLGESCHLAGEN, PersonenKonfliktStatus.MANUELL_ZU_PRUEFEN, betroffenePersonen);
  }

  /**
   * @param konfliktArt
   * @param konfliktStatus
   * @param betroffenePersonen
   *          Bei keiner Beschräkung <code>null</code> übergeben.
   */
  private void resetPersonenKonflikt(PersonenKonfliktArt konfliktArt, PersonenKonfliktStatus konfliktStatus, List< ? extends Personendaten> betroffenePersonen) {
    resetPersonenKonflikt(asList(konfliktArt), asList(konfliktStatus), betroffenePersonen);
  }

  /**
   *
   * @param wahl
   * @param konfliktArt
   * @param konfliktStatus
   * @param betroffenePersonen
   *          Bei keiner Beschräkung <code>null</code> übergeben.
   */
  private void resetPersonenKonflikt(Wahl wahl, PersonenKonfliktArt konfliktArt, PersonenKonfliktStatus konfliktStatus, List< ? extends Personendaten> betroffenePersonen) {
    resetPersonenKonflikt(wahl, asList(konfliktArt), asList(konfliktStatus), betroffenePersonen);
  }

  /**
   * Sorgt für eine Neuprüfung aller Personen in den bezeichneten Konflikten.
   *
   * @param konfliktArten
   * @param konfliktStatus
   * @param betroffenePersonen
   *          Bei keiner Beschräkung <code>null</code> übergeben.
   */
  private void resetPersonenKonflikt(Collection<PersonenKonfliktArt> konfliktArten, Collection<PersonenKonfliktStatus> konfliktStatus, List< ? extends Personendaten> betroffenePersonen) {
    resetPersonenKonfliktPersonendaten(dao.findByKonfliktArtAndKonfliktStatus(konfliktArten, konfliktStatus), betroffenePersonen);
  }

  /**
   * Sorgt für eine Neuprüfung aller Personen in den bezeichneten Konflikten.
   *
   * @param konfliktArten
   * @param konfliktStatus
   * @param betroffenePersonen
   *          Bei keiner Beschräkung <code>null</code> übergeben.
   */
  private void resetPersonenKonflikt(Wahl wahl, Collection<PersonenKonfliktArt> konfliktArten, Collection<PersonenKonfliktStatus> konfliktStatus, List< ? extends Personendaten> betroffenePersonen) {
    resetPersonenKonfliktPersonendaten(dao.findByWahlAndKonfliktArtAndKonfliktStatus(wahl, konfliktArten, konfliktStatus), betroffenePersonen);
  }

  /**
   *
   * @param konflikte
   * @param betroffenePersonen
   *          Bei keiner Beschräkung <code>null</code> übergeben.
   */
  private void resetPersonenKonfliktPersonendaten(Collection<PersonenKonflikt> konflikte, List< ? extends Personendaten> betroffenePersonen) {
    for (PersonenKonflikt konflikt : konflikte) {
      Personendaten personendaten = konflikt.getPersonendaten();

      if (isBetroffen(personendaten, betroffenePersonen) && personendaten.isPersonStatusManuellZuPruefenderKonflikt()) {
        personendaten.setPersonStatus(PersonStatus.UNGEPRUEFT);

        if (PersonenKonfliktArt.ABGLEICH_FEHLGESCHLAGEN.equals(konflikt.getPersonenKonfliktArt())) {
          personendaten.setPersonenregisterAbgleichStatus(PersonenregisterAbgleichStatus.ABGLEICH_NOTWENDIG);
        }
      }
    }
  }

  private boolean isBetroffen(Personendaten personendaten, List< ? extends Personendaten> betroffenePersonendaten) {
    return Objects.nonNull(betroffenePersonendaten) && betroffenePersonendaten.contains(personendaten);
  }

  public void delete(List<PersonenKonflikt> zuLoeschendeKonflikte) {
    zuLoeschendeKonflikte.forEach(this::delete);
  }

  public void delete(Personendaten personendaten, PersonenKonfliktArt konfliktArt) {
    delete(personendaten, new HashSet<>(asList(konfliktArt)));
  }

  public void delete(Personendaten personendaten, Set<PersonenKonfliktArt> konfliktArten) {
    // NOTE ELECTMUC-902, dass pro Person nur maximal ein Konflikt per Konfliktart existiert! D.h. für eine Person darf
    // eine Konfliktart nicht mehrmals in derselben Transaktion gelöscht werden, da das Löschen des jeweiligen Konfliktes
    // für den Finder NICHT sichtbar ist! Die Verwendung von DAO#flush ist KEINE Option, da diese Methode auch bei
    // Bulk-Operationen auf Personendaten, bspw. 'Personenregister-Abgleich erneut starten', verwendet wird!
    if (!konfliktArten.isEmpty()) {
      for (PersonenKonflikt konflikt : dao.findByPersonAndKonfliktArt(personendaten, konfliktArten)) {
        delete(konflikt);
      }
    }
  }

  public void deletePersonenKonfliktWertelistenAbgleichAndSetPersonStatusUngeprueft(List< ? extends Personendaten> personen) {
    deletePersonenKonfliktAndSetPersonStatusUngeprueft(personen, PersonenKonfliktArt.WERTELISTEN_ABGLEICH_FEHLGESCHLAGEN);
  }

  protected void deletePersonenKonfliktAndSetPersonStatusUngeprueft(List< ? extends Personendaten> personen, PersonenKonfliktArt konfliktArt) {
    for (Personendaten personendaten : personen) {
      delete(personendaten, konfliktArt);

      personendaten.setPersonStatus(PersonStatus.UNGEPRUEFT);
    }
  }

  public void updatePersonendatenAndKonflikt(PersonStatus personStatus, PersonenKonfliktStatus konfliktStatus, PersonenKonflikt konflikt) {
    updatePersonendatenAndKonflikt(personStatus, konfliktStatus, Arrays.asList(konflikt));
  }

  public void updatePersonendatenAndKonflikt(PersonStatus personStatus, PersonenKonfliktStatus konfliktStatus, Collection<PersonenKonflikt> konflikte) {
    for (PersonenKonflikt konflikt : konflikte) {
      updatePersonendatenAndKonfliktInternal(personStatus, konfliktStatus, konflikt);
    }

    // Löse mögliche weitere Prüfungen aus.
    startePersonenPruefung();
  }

  protected void startePersonenPruefung() {
    personenPruefungStarter.startePersonenPruefung();
  }

  protected void updatePersonendatenAndKonfliktInternal(PersonStatus personStatus, PersonenKonfliktStatus konfliktStatus, PersonenKonflikt konflikt) {
    PersonenKonflikt attachedKonflikt = getAttachedEntity(konflikt);

    attachedKonflikt.getPersonendaten().setPersonStatus(personStatus);
    attachedKonflikt.setPersonenKonfliktStatus(konfliktStatus);

    if (PersonStatus.GEEIGNET.equals(personStatus)) {
      // NOTE, dass nur ungeprüfte Personendaten für mögliche weitere Prüfungen gefunden werden!
      attachedKonflikt.getPersonendaten().setPersonStatus(PersonStatus.UNGEPRUEFT);
    }

    if (PersonStatus.NICHT_GEEIGNET.equals(personStatus) && PersonenKonfliktStatus.IST_ABLEHNUNGSGRUND.equals(konfliktStatus)) {
      // NOTE ELECT-2773, dass beim Ablehnen alle anderen Konflikte gelöscht werden sollen
      delete(attachedKonflikt.getPersonendaten(), alleAusser(attachedKonflikt.getPersonenKonfliktArt()));
    }
  }

  private static Set<PersonenKonfliktArt> alleAusser(PersonenKonfliktArt personenKonfliktArt) {
    Set<PersonenKonfliktArt> result = new HashSet<>(asList(PersonenKonfliktArt.values()));
    result.remove(personenKonfliktArt);

    return result;
  }
}
