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

import static java.util.Collections.emptySet;

import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.business.person.entity.PersonendatenPK;
import de.ivu.elect.business.veranstaltung.entity.Teilnahme;

public interface TeilnahmePersonendatenFinder {

  /**
   * HINT: Aufrufen, wenn die tatsächliche Person noch nicht benötigt wird, sondern lediglich ihr Vorhandensein zu prüfen ist. Überschreiben, falls hier ein anderer Workflow angewendet werden kann.
   *
   * @deprecated ELECT-8917 Unklar, ob dieser Mechanismus benötigt wird...
   * @see TeilnahmeImporter#validateTeilnahmenFuerVeranstaltung
   */
  @Deprecated
  default Result validatePersonendatenInDb(Teilnahme teilnahmeInDatei) {
    return findPersonendatenInDb(teilnahmeInDatei);
  }

  Result findPersonendatenInDb(Teilnahme teilnahmeInDatei);

  interface Result {

    boolean isContainedIn(Set<PersonendatenPK> pkSet);

    Personendaten getPersonendatenEindeutig();

    default boolean isErgebnisMehrdeutigOderLeer() {
      return !isErgebnisEindeutig();
    }

    default boolean isErgebnisNichtLeer() {
      return !isErgebnisLeer();
    }

    default boolean isErgebnisLeer() {
      return getAnzahlPersonendaten() == 0L;
    }

    default boolean isErgebnisMehrdeutig() {
      return getAnzahlPersonendaten() > 1L;
    }

    default boolean isErgebnisEindeutig() {
      return getAnzahlPersonendaten() == 1L;
    }

    long getAnzahlPersonendaten();
  }

  class FindResult implements Result {

    private final Collection<Personendaten> personendaten;

    public FindResult() {
      this(emptySet());
    }

    public FindResult(Collection<Personendaten> personendaten) {
      super();

      this.personendaten = personendaten;
    }

    @Override
    public boolean isContainedIn(Set<PersonendatenPK> pkSet) {
      return pkSet.contains(getPersonendatenEindeutig().getPk());
    }

    @Override
    public Personendaten getPersonendatenEindeutig() {
      if (!isErgebnisEindeutig()) {
        throw new IllegalStateException("Ergebnis nicht eindeutig: " + personendaten.stream().map(Personendaten::toString).collect(Collectors.joining(", ")));
      }

      return personendaten.iterator().next();
    }

    @Override
    public boolean isErgebnisLeer() {
      return personendaten.isEmpty();
    }

    @Override
    public long getAnzahlPersonendaten() {
      return personendaten.size();
    }

    @Override
    public String toString() {
      return "FindResult{" + "personendaten=" + personendaten + '}';
    }
  }

  class ValidateResult implements Result {

    private final long anzahlPersonendaten;

    public ValidateResult(long anzahlPersonendaten) {
      this.anzahlPersonendaten = anzahlPersonendaten;
    }

    @Override
    public boolean isContainedIn(Set<PersonendatenPK> pkSet) {
      throw new UnsupportedOperationException("Nicht möglich für ein Vorab-Ergebnis");
    }

    @Override
    public Personendaten getPersonendatenEindeutig() {
      throw new UnsupportedOperationException("Nicht möglich für ein Vorab-Ergebnis");
    }

    @Override
    public long getAnzahlPersonendaten() {
      return anzahlPersonendaten;
    }

    @Override
    public String toString() {
      return "ValidateResult{" + "anzahlPersonendaten=" + anzahlPersonendaten + '}';
    }
  }
}
