package de.ivu.elect.sitzberechnung.control;

import de.ivu.elect.sitzberechnung.SitzberechnungEntity;

import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;

public abstract class AbstractSitzberechnungsdaten {
  public abstract void validate();

  protected void verifyEindeutigenKeys(Collection<? extends SitzberechnungEntity> entitaeten) {
    verifyKeinNullEntitaetenUndKeinNullKeys(entitaeten);

    long anzahlEindeutigenKeys = entitaeten.stream()
            .map(SitzberechnungEntity::getKey)
            .distinct().count();

    if (anzahlEindeutigenKeys != entitaeten.size()) {
      String anzahlString = entitaeten.stream()
              .collect(Collectors.groupingBy(SitzberechnungEntity::getKey, Collectors.counting())).entrySet().stream()
              .filter(e -> e.getValue() > 1).map(e -> String.format("(%s %sx)", e.getKey(), e.getValue()))
              .collect(Collectors.joining(", "));
      throw new IllegalStateException(String.format("Entitäten (vom Typ(en): %s) müssen eindeutige Keys haben [%s]",
              getEntityTypenAlsString(entitaeten), anzahlString));
    }
  }

  void verifyKeinNullEntitaetenUndKeinNullKeys(Collection<? extends SitzberechnungEntity> entitaeten) {
    if (entitaeten.stream().anyMatch(Objects::isNull)) {
      throw new IllegalStateException(String.format("Eine eingegebene Entität vom Typ %s ist null.", getEntityTypenAlsString(entitaeten)));
    }
    if (entitaeten.stream().map(SitzberechnungEntity::getKey).anyMatch(Objects::isNull)) {
      throw new IllegalStateException(String.format("Keys der Entitäten (vom Typ: %s) müssen nicht null sein!", getEntityTypenAlsString(entitaeten)));
    }
  }

  private String getEntityTypenAlsString(Collection<? extends SitzberechnungEntity> entitaeten) {
    if (entitaeten.isEmpty()) {
      return "";
    }
    return entitaeten.stream()
            .filter(Objects::nonNull)
            .flatMap(e -> {
              Class<?> clazz = e.getClass();
              while (clazz.getInterfaces().length == 0) {
                if (clazz.getSuperclass() == null) {
                  break;
                }
                clazz = clazz.getSuperclass();
              }
              return Arrays.stream(clazz.getInterfaces());
            })
            .distinct()
            .map(Class::getName)
            .collect(Collectors.joining(","));
  }


}
