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

import java.io.Serializable;
import java.util.Optional;
import java.util.function.BooleanSupplier;

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

import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.security.control.ElectCommonPrincipalOderSystem;
import de.ivu.elect.business.stimmeingabe.control.plausi.StimmeingabePlausiGebietsartResolver;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiGebietsart;
import de.ivu.elect.business.wahl.boundary.WasUmgangAWerteKonfiguration;
import de.ivu.elect.business.wahl.boundary.WasUmgangBWerteKonfiguration;
import de.ivu.elect.business.wahl.entity.UmgangAWerte;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;

// Cf. https://support.elect-it.com/wiki/display/ELECT/Kopfzahlenkonfiguration.
public class StimmeingabeKopfzahlKonfiguration implements Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient ElectCommonPrincipalOderSystem principal;

  @Inject
  private transient WasUmgangAWerteKonfiguration umgangAWerteKonfiguration;

  @Inject
  private transient WasUmgangBWerteKonfiguration umgangBWerteKonfiguration;

  @Inject
  private transient StimmeingabePlausiGebietsartResolver stimmeingabePlausiGebietsartResolver;

  public StimmeingabeKopfzahlKonfiguration() {
    super();
  }

  public boolean isPruefkennzeichenWahlberechtigteImportNotwendig(Wahldurchfuehrung wahldurchfuehrung) {
    return getUmgangAWerteKonfiguration().isPruefkennzeichenWahlberechtigteImportNotwendig(wahldurchfuehrung);
  }

  public boolean isPruefkennzeichenWahlberechtigteBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isPruefkennzeichenWahlberechtigteBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isPruefkennzeichenWahlberechtigteBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isPruefkennzeichenWahlberechtigteBeiStimmeingabeInitialVorzubelegen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    UmgangAWerte umgangAWerte = getUmgangPruefkennzeichenWahlberechtigte(wahldurchfuehrungsgebiet, ergebnisart);

    return isPruefkennzeichenWahlberechtigteBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && isAnzahlWahlberechtigteBeiStimmeingabeInitialVorzubelegen(umgangAWerte, wahldurchfuehrungsgebiet::hasPruefkennzeichen);
  }

  public boolean isPruefkennzeichenWahlberechtigteBeiStimmeingabeNurAnzuzeigen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    UmgangAWerte umgangAWerte = getUmgangPruefkennzeichenWahlberechtigte(wahldurchfuehrungsgebiet, ergebnisart);

    return isPruefkennzeichenWahlberechtigteBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && isAnzahlWahlberechtigteBeiStimmeingabeAnzuzeigen(umgangAWerte, wahldurchfuehrungsgebiet::hasPruefkennzeichen);
  }

  public boolean isPruefkennzeichenWahlberechtigteBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return !hasUseCasePermissionOhnePruefkennzifferAnlegen() && isPruefkennzeichenWahlberechtigteBeiGebietRelevant(wahldurchfuehrungsgebiet) && getUmgangAWerteKonfiguration()
        .isPruefkennzeichenWahlberechtigteBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isPruefkennzeichenWahlberechtigteBeiStimmeingabeZuPlausibilisierenGegenPruefkennzeichenWahlberechtigteAmGebiet(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
      WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return !hasUseCasePermissionOhnePruefkennzifferAnlegen() && isPruefkennzeichenWahlberechtigteBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isPruefkennzeichenWahlberechtigteBeiStimmeingabeZuPlausibilisierenGegenPruefkennzeichenWahlberechtigteAmGebiet(wahldurchfuehrung, ergebnisart,
            findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  private boolean hasUseCasePermissionOhnePruefkennzifferAnlegen() {
    return hasUseCasePermission(UseCasePermission.STIMMEINGABE_CREATE_OHNE_PRUEFKENNZEICHEN);
  }

  private boolean isPruefkennzeichenWahlberechtigteBeiGebietRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getUmgangAWerteKonfiguration().isPruefkennzeichenWahlberechtigteBeiGebietsartRelevant(wahldurchfuehrungsgebiet);
  }

  protected UmgangAWerte getUmgangPruefkennzeichenWahlberechtigte(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangAWerteKonfiguration().getUmgangPruefkennzeichenWahlberechtigte(wahldurchfuehrung, ergebnisart);
  }

  public boolean isAnyAnzahlWahlberechtigteAmGebietErfassbar(Wahldurchfuehrung wahldurchfuehrung) {
    return getUmgangAWerteKonfiguration().isAnyAnzahlWahlberechtigteAmGebietErfassbar(wahldurchfuehrung);
  }

  public boolean isAnyAnzahlWahlberechtigteImportNotwendig(Wahldurchfuehrung wahldurchfuehrung) {
    return getUmgangAWerteKonfiguration().isAnyAnzahlWahlberechtigteImportNotwendig(wahldurchfuehrung);
  }

  public boolean isWahlberechtigteOhneWahlscheinBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteOhneWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteOhneWahlscheinBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWahlberechtigteOhneWahlscheinBeiStimmeingabeInitialVorzubelegen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteOhneWahlschein(wahldurchfuehrungsgebiet, ergebnisart);

    return isWahlberechtigteOhneWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && isAnzahlWahlberechtigteBeiStimmeingabeInitialVorzubelegen(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteOhneWahlscheinBeiStimmeingabeNurAnzuzeigen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteOhneWahlschein(wahldurchfuehrungsgebiet, ergebnisart);

    return isWahlberechtigteOhneWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet) && isAnzahlWahlberechtigteBeiStimmeingabeAnzuzeigen(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteOhneWahlscheinBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteOhneWahlschein(wahldurchfuehrungsgebiet, ergebnisart);
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteOhneWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteOhneWahlscheinBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet))
        && isAnzahlWahlberechtigteBeiStimmeingabeZuPlausibilisieren(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteOhneWahlscheinBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteOhneWahlscheinAmGebiet(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
      WahlartErgebnisart ergebnisart, StimmeingabeKopfzahlKonfigurationContext context) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteOhneWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteOhneWahlscheinBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteOhneWahlscheinAmGebiet(wahldurchfuehrung, ergebnisart,
            findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet))
        && isWahlberechtigteOhneWahlscheinAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  private boolean isWahlberechtigteOhneWahlscheinAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return wahldurchfuehrungsgebiet != null && wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteOhneWahlschein(context.getErfasstFuer());
  }

  public long extractWahlberechtigteOhneWahlscheinAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return fixNullToZero(extractWahlberechtigteOhneWahlscheinAmGebietHinterlegtOrNull(wahldurchfuehrungsgebiet, ergebnisart, context));
  }

  @Nullable
  public Long extractWahlberechtigteOhneWahlscheinAmGebietHinterlegtOrNull(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return wahldurchfuehrungsgebiet.getAnzahlWahlberechtigteOhneWahlschein(context.getErfasstFuer());
  }

  private boolean isWahlberechtigteOhneWahlscheinBeiGebietRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getUmgangAWerteKonfiguration().isWahlberechtigteOhneWahlscheinBeiGebietsartRelevant(wahldurchfuehrungsgebiet);
  }

  protected UmgangAWerte getUmgangWahlberechtigteOhneWahlschein(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangAWerteKonfiguration().getUmgangWahlberechtigteOhneWahlschein(wahldurchfuehrung, ergebnisart);
  }

  public boolean isWahlberechtigteMitWahlscheinBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteMitWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteMitWahlscheinBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWahlberechtigteMitWahlscheinBeiStimmeingabeInitialVorzubelegen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteMitWahlschein(wahldurchfuehrungsgebiet, ergebnisart);

    return isWahlberechtigteMitWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && isAnzahlWahlberechtigteBeiStimmeingabeInitialVorzubelegen(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteMitWahlscheinBeiStimmeingabeNurAnzuzeigen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteMitWahlschein(wahldurchfuehrungsgebiet, ergebnisart);

    return isWahlberechtigteMitWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet) && isAnzahlWahlberechtigteBeiStimmeingabeAnzuzeigen(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteMitWahlscheinBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteMitWahlschein(wahldurchfuehrungsgebiet, ergebnisart);
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteMitWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteMitWahlscheinBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet))
        && isAnzahlWahlberechtigteBeiStimmeingabeZuPlausibilisieren(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteMitWahlscheinBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteMitWahlscheinAmGebiet(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
      WahlartErgebnisart ergebnisart, StimmeingabeKopfzahlKonfigurationContext context) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteMitWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteMitWahlscheinBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteMitWahlscheinAmGebiet(wahldurchfuehrung, ergebnisart,
            findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet))
        && isWahlberechtigteMitWahlscheinAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  private boolean isWahlberechtigteMitWahlscheinAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return wahldurchfuehrungsgebiet != null && wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteMitWahlschein(context.getErfasstFuer());
  }

  public long extractWahlberechtigteMitWahlscheinAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return fixNullToZero(extractWahlberechtigteMitWahlscheinAmGebietHinterlegtOrNull(wahldurchfuehrungsgebiet, ergebnisart, context));
  }

  @Nullable
  public Long extractWahlberechtigteMitWahlscheinAmGebietHinterlegtOrNull(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return wahldurchfuehrungsgebiet.getAnzahlWahlberechtigteMitWahlschein(context.getErfasstFuer());
  }

  private boolean isWahlberechtigteMitWahlscheinBeiGebietRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getUmgangAWerteKonfiguration().isWahlberechtigteMitWahlscheinBeiGebietsartRelevant(wahldurchfuehrungsgebiet);
  }

  protected UmgangAWerte getUmgangWahlberechtigteMitWahlschein(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangAWerteKonfiguration().getUmgangWahlberechtigteMitWahlschein(wahldurchfuehrung, ergebnisart);
  }

  public boolean isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteNichtImWaehlerverzeichnisBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeInitialVorzubelegen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteNichtImWaehlerverzeichnis(wahldurchfuehrungsgebiet, ergebnisart);

    return isWahlberechtigteNichtImWaehlerverzeichnisBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && isAnzahlWahlberechtigteBeiStimmeingabeInitialVorzubelegen(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeNurAnzuzeigen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteNichtImWaehlerverzeichnis(wahldurchfuehrungsgebiet, ergebnisart);

    return isWahlberechtigteNichtImWaehlerverzeichnisBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && isAnzahlWahlberechtigteBeiStimmeingabeAnzuzeigen(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteNichtImWaehlerverzeichnis(wahldurchfuehrungsgebiet, ergebnisart);
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteNichtImWaehlerverzeichnisBeiGebietRelevant(wahldurchfuehrungsgebiet) && getUmgangAWerteKonfiguration()
        .isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet))
        && isAnzahlWahlberechtigteBeiStimmeingabeZuPlausibilisieren(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteNichtImWaehlerverzeichnisAmGebiet(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
      WahlartErgebnisart ergebnisart, StimmeingabeKopfzahlKonfigurationContext context) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteNichtImWaehlerverzeichnisBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteNichtImWaehlerverzeichnisAmGebiet(wahldurchfuehrung,
            ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet))
        && isWahlberechtigteNichtImWaehlerverzeichnisAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  private boolean isWahlberechtigteNichtImWaehlerverzeichnisAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return wahldurchfuehrungsgebiet != null && wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteNichtImWaehlerverzeichnis(context.getErfasstFuer());
  }

  public long extractWahlberechtigteNichtImWaehlerverzeichnisAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return fixNullToZero(extractWahlberechtigteNichtImWaehlerverzeichnisAmGebietHinterlegtOrNull(wahldurchfuehrungsgebiet, ergebnisart, context));
  }

  @Nullable
  public Long extractWahlberechtigteNichtImWaehlerverzeichnisAmGebietHinterlegtOrNull(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return wahldurchfuehrungsgebiet.getAnzahlWahlberechtigteNichtImWaehlerverzeichnis(context.getErfasstFuer());
  }

  private boolean isWahlberechtigteNichtImWaehlerverzeichnisBeiGebietRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getUmgangAWerteKonfiguration().isWahlberechtigteNichtImWaehlerverzeichnisBeiGebietsartRelevant(wahldurchfuehrungsgebiet);
  }

  protected UmgangAWerte getUmgangWahlberechtigteNichtImWaehlerverzeichnis(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangAWerteKonfiguration().getUmgangWahlberechtigteNichtImWaehlerverzeichnis(wahldurchfuehrung, ergebnisart);
  }

  public boolean isWahlberechtigteGesamtBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteGesamtBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteGesamtBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWahlberechtigteGesamtBeiStimmeingabeInitialVorzubelegen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteGesamt(wahldurchfuehrungsgebiet, ergebnisart);

    return isWahlberechtigteGesamtBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && isAnzahlWahlberechtigteBeiStimmeingabeInitialVorzubelegen(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteGesamtBeiStimmeingabeNurAnzuzeigen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteGesamt(wahldurchfuehrungsgebiet, ergebnisart);

    return isWahlberechtigteGesamtBeiGebietRelevant(wahldurchfuehrungsgebiet) && isAnzahlWahlberechtigteBeiStimmeingabeAnzuzeigen(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteGesamtBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    UmgangAWerte umgangAWerte = getUmgangWahlberechtigteGesamt(wahldurchfuehrungsgebiet, ergebnisart);
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteGesamtBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangAWerteKonfiguration().isWahlberechtigteGesamtBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet))
        && isAnzahlWahlberechtigteBeiStimmeingabeZuPlausibilisieren(umgangAWerte, wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWahlberechtigteGesamtBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteGesamtAmGebiet(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWahlberechtigteGesamtBeiGebietRelevant(wahldurchfuehrungsgebiet) && getUmgangAWerteKonfiguration()
        .isWahlberechtigteGesamtBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteGesamtAmGebiet(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet))
        && isWahlberechtigteGesamtAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  private boolean isWahlberechtigteGesamtAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart, StimmeingabeKopfzahlKonfigurationContext context) {
    return wahldurchfuehrungsgebiet != null && extractWahlberechtigteGesamtAmGebietHinterlegtOrNull(wahldurchfuehrungsgebiet, ergebnisart, context) != null;
  }

  public long extractWahlberechtigteGesamtAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart, StimmeingabeKopfzahlKonfigurationContext context) {
    return fixNullToZero(extractWahlberechtigteGesamtAmGebietHinterlegtOrNull(wahldurchfuehrungsgebiet, ergebnisart, context));
  }

  @Nullable
  public Long extractWahlberechtigteGesamtAmGebietHinterlegtOrNull(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    if (isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart)) {
      return wahldurchfuehrungsgebiet.getAnzahlWahlberechtigterGesamtA1A2A3(context.getErfasstFuer());
    }

    return wahldurchfuehrungsgebiet.getAnzahlWahlberechtigterA1A2(context.getErfasstFuer());
  }

  private boolean isWahlberechtigteGesamtBeiGebietRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getUmgangAWerteKonfiguration().isWahlberechtigteGesamtBeiGebietsartRelevant(wahldurchfuehrungsgebiet);
  }

  protected UmgangAWerte getUmgangWahlberechtigteGesamt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangAWerteKonfiguration().getUmgangWahlberechtigteGesamt(wahldurchfuehrung, ergebnisart);
  }

  private boolean isAnzahlWahlberechtigteBeiStimmeingabeInitialVorzubelegen(UmgangAWerte umgangAWerte, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return isAnzahlWahlberechtigteBeiStimmeingabeInitialVorzubelegen(umgangAWerte, () -> isAnyAnzahlWahlberechtigteAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context));
  }

  private boolean isAnzahlWahlberechtigteBeiStimmeingabeInitialVorzubelegen(UmgangAWerte umgangAWerte, BooleanSupplier existsAnyWertAmGebiet) {
    return umgangAWerte.isAWertBeiStimmeingabeInitialVorzubelegen();
  }

  private boolean isAnzahlWahlberechtigteBeiStimmeingabeZuPlausibilisieren(UmgangAWerte umgangAWerte, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return isAnzahlWahlberechtigteBeiStimmeingabeZuPlausibilisieren(umgangAWerte, () -> isAnyAnzahlWahlberechtigteAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context));
  }

  private boolean isAnzahlWahlberechtigteBeiStimmeingabeZuPlausibilisieren(UmgangAWerte umgangAWerte, BooleanSupplier existsAnyWertAmGebiet) {
    return isAnzahlWahlberechtigteBeiStimmeingabeZuErfassen(umgangAWerte, existsAnyWertAmGebiet);
  }

  private boolean isAnzahlWahlberechtigteBeiStimmeingabeAnzuzeigen(UmgangAWerte umgangAWerte, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart,
      StimmeingabeKopfzahlKonfigurationContext context) {
    return isAnzahlWahlberechtigteBeiStimmeingabeAnzuzeigen(umgangAWerte, () -> isAnyAnzahlWahlberechtigteAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context));
  }

  private boolean isAnzahlWahlberechtigteBeiStimmeingabeAnzuzeigen(UmgangAWerte umgangAWerte, BooleanSupplier existsAnyWertAmGebiet) {
    return umgangAWerte.isAWertBeiStimmeingabeImmerNurAnzuzeigen() //
        || existsAnyWertAmGebiet.getAsBoolean() && umgangAWerte.isAWertBeiStimmeingabeInitialVorzubelegen() && !hasUseCasePermissionAnzahlWahlberechtigteBeiStimmeingabeBearbeiten(); // ELECTKOM-993
  }

  private boolean isAnzahlWahlberechtigteBeiStimmeingabeZuErfassen(UmgangAWerte umgangAWerte, BooleanSupplier existsAnyWertAmGebiet) {
    return umgangAWerte.isAWertBeiStimmeingabeZuErfassen();
  }

  private boolean hasUseCasePermissionAnzahlWahlberechtigteBeiStimmeingabeBearbeiten() {
    return hasUseCasePermission(UseCasePermission.ANZAHL_WAHLBERECHTIGTE_EDIT);
  }

  private boolean isAnyAnzahlWahlberechtigteAmGebietHinterlegt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart, StimmeingabeKopfzahlKonfigurationContext context) {
    // NOTE, dass "Wahlberechtigte insgesamt" nur aus diesen Werten berechnet wird...
    return isWahlberechtigteOhneWahlscheinAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context)
        || isWahlberechtigteMitWahlscheinAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context)
        || isWahlberechtigteNichtImWaehlerverzeichnisAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebnisart, context);
  }

  public boolean isWaehlerOhneWahlscheinBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWaehlerOhneWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangBWerteKonfiguration().isWaehlerOhneWahlscheinBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWaehlerOhneWahlscheinBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWaehlerOhneWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangBWerteKonfiguration().isWaehlerOhneWahlscheinBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  private boolean isWaehlerOhneWahlscheinBeiGebietRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getUmgangBWerteKonfiguration().isWaehlerOhneWahlscheinBeiGebietsartRelevant(wahldurchfuehrungsgebiet);
  }

  public boolean isWaehlerMitWahlscheinBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWaehlerMitWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWaehlerMitWahlscheinBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWaehlerMitWahlscheinBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  private boolean isWaehlerMitWahlscheinBeiGebietRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinBeiGebietsartRelevant(wahldurchfuehrungsgebiet);
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietRelevantAndConfigured(wahldurchfuehrungsgebiet, ergebnisart,
        wahldurchfuehrungsgebiet::isBriefwahlInUrneErfassenInGebietOderDarunter)
        && getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart,
            findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietRelevantAndConfigured(wahldurchfuehrungsgebiet, ergebnisart,
        wahldurchfuehrungsgebiet::isBriefwahlInUrneErfassenInGebietOderDarunter)
        && getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart,
            findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  // ELECTDD-331 - B2 Werte müssen erfasst werden können
  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietRelevantAndConfigured(wahldurchfuehrungsgebiet, ergebnisart,
        wahldurchfuehrungsgebiet::isBriefwahlInUrneErfassenInGebietOderDarunter)
        && getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart,
            findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietRelevantAndConfigured(wahldurchfuehrungsgebiet, ergebnisart,
        wahldurchfuehrungsgebiet::isBriefwahlInUrneErfassenInGebietOderDarunter)
        && getUmgangBWerteKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart,
            findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  /**
   * @deprecated Hat hier eigentlich nichts zu suchen, da der Rückgabewert das Layout bestimmt, muss dafür aber nur einmalig für MUC überschrieben werden!
   */
  @Deprecated
  public boolean isWaehlerGesamtBeiStimmeingabeAlsErstesZuErfassen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return true;
  }

  public boolean isWaehlerGesamtBeiStimmeingabeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWaehlerGesamtBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangBWerteKonfiguration().isWaehlerGesamtBeiStimmeingabeRelevant(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  public boolean isWaehlerGesamtBeiStimmeingabeZuPlausibilisieren(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

    return isWaehlerGesamtBeiGebietRelevant(wahldurchfuehrungsgebiet)
        && getUmgangBWerteKonfiguration().isWaehlerGesamtBeiStimmeingabeZuPlausibilisieren(wahldurchfuehrung, ergebnisart, findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet));
  }

  private boolean isWaehlerGesamtBeiGebietRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getUmgangBWerteKonfiguration().isWaehlerGesamtBeiGebietsartRelevant(wahldurchfuehrungsgebiet);
  }

  public Optional<StimmeingabePlausiGebietsart> findStimmeingabePlausiGebietsart(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getStimmeingabePlausiGebietsartResolver().findStimmeingabePlausiGebietsart(wahldurchfuehrungsgebiet);
  }

  protected StimmeingabePlausiGebietsartResolver getStimmeingabePlausiGebietsartResolver() {
    return stimmeingabePlausiGebietsartResolver;
  }

  public WasUmgangAWerteKonfiguration getUmgangAWerteKonfiguration() {
    return umgangAWerteKonfiguration;
  }

  public WasUmgangBWerteKonfiguration getUmgangBWerteKonfiguration() {
    return umgangBWerteKonfiguration;
  }

  private boolean hasUseCasePermission(UseCasePermission useCasePermission) {
    return principal.hasUseCasePermission(useCasePermission);
  }

  private long fixNullToZero(Long value) {
    if (value == null) {
      return 0L;
    }

    return value;
  }
}
