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

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

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiGebietsart;
import de.ivu.elect.business.wahl.entity.UmgangBWerte;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahlart;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;

public class UmgangBWerteKonfiguration implements Serializable {

  private static final long serialVersionUID = 1L;

  public UmgangBWerteKonfiguration() {
    super();
  }

  public UmgangBWerte getUmgangWaehlerOhneWahlschein(Wahl wahl, WahlartErgebnisart ergebnisart) {
    Wahlart wahlart = wahl.getWahlart();

    if (ergebnisart.isEndgueltig()) {
      return wahlart.getUmgangBOhneWahlscheinEndgueltig();
    }

    if (ergebnisart.isVorlaeufig()) {
      return wahlart.getUmgangBOhneWahlscheinVorlaeufig();
    }

    throw new IllegalArgumentException("Unbekannte Ergebnisart: " + ergebnisart);
  }

  public UmgangBWerte getUmgangWaehlerMitWahlschein(Wahl wahl, WahlartErgebnisart ergebnisart) {
    Wahlart wahlart = wahl.getWahlart();

    if (ergebnisart.isEndgueltig()) {
      return wahlart.getUmgangBMitWahlscheinEndgueltig();
    }

    if (ergebnisart.isVorlaeufig()) {
      return wahlart.getUmgangBMitWahlscheinVorlaeufig();
    }

    throw new IllegalArgumentException("Unbekannte Ergebnisart: " + ergebnisart);
  }

  public UmgangBWerte getUmgangWaehlerGesamt(Wahl wahl, WahlartErgebnisart ergebnisart) {
    Wahlart wahlart = wahl.getWahlart();

    if (ergebnisart.isEndgueltig()) {
      return wahlart.getUmgangBGesamtEndgueltig();
    }

    if (ergebnisart.isVorlaeufig()) {
      return wahlart.getUmgangBGesamtVorlaeufig();
    }

    throw new IllegalArgumentException("Unbekannte Ergebnisart: " + ergebnisart);
  }

  public boolean isWaehlerOhneWahlscheinBeiStimmeingabeRelevant(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerOhneWahlscheinBeiGebietsartRelevant(gebietsart) && getUmgangWaehlerOhneWahlschein(wahl, ergebnisart).isBWertBeiStimmeingabeZuErfassenOderAnzuzeigen();
  }

  public boolean isWaehlerOhneWahlscheinBeiStimmeingabeZuPlausibilisieren(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerOhneWahlscheinBeiGebietsartRelevant(gebietsart) && getUmgangWaehlerOhneWahlschein(wahl, ergebnisart).isBWertBeiStimmeingabeZuErfassen();
  }

  public boolean isWaehlerOhneWahlscheinBeiGebietsartRelevant(Gebiet gebiet) {
    return isWaehlerOhneWahlscheinBeiGebietsartRelevant(findStimmeingabePlausiGebietsart(gebiet));
  }

  public boolean isWaehlerOhneWahlscheinBeiGebietsartRelevant(Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return !isBriefwahlbezirk(gebietsart);
  }

  /**
   * @deprecated Nur zur Anzeige "potentieller" Plausis verwenden, da "Wähler ohne Wahlschein (berechnet)" keine tatsächliche Stimmeingabe ist, sondern ein Wert, der anhand von "Wähler insgesamt" und
   *             "Wähler mit Wahlschein" berechnet wird! Alle Plausis hängen dann an "Wähler insgesamt".
   */
  @Deprecated
  public boolean isWaehlerOhneWahlscheinBerechnetBeiStimmeingabeZuPlausibilisieren(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerGesamtBeiStimmeingabeZuPlausibilisieren(wahl, ergebnisart, gebietsart) && isWaehlerMitWahlscheinBeiStimmeingabeZuPlausibilisieren(wahl, ergebnisart, gebietsart);
  }

  public boolean isWaehlerMitWahlscheinBeiStimmeingabeRelevant(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerMitWahlscheinBeiGebietsartRelevant(gebietsart) && getUmgangWaehlerMitWahlschein(wahl, ergebnisart).isBWertBeiStimmeingabeZuErfassenOderAnzuzeigen();
  }

  public boolean isWaehlerMitWahlscheinBeiStimmeingabeZuPlausibilisieren(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerMitWahlscheinBeiGebietsartRelevant(gebietsart) && getUmgangWaehlerMitWahlschein(wahl, ergebnisart).isBWertBeiStimmeingabeZuErfassen();
  }

  public boolean isWaehlerMitWahlscheinBeiGebietsartRelevant(Gebiet gebiet) {
    return isWaehlerMitWahlscheinBeiGebietsartRelevant(findStimmeingabePlausiGebietsart(gebiet));
  }

  public boolean isWaehlerMitWahlscheinBeiGebietsartRelevant(Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return !isBriefwahlbezirk(gebietsart);
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietRelevantAndConfigured(Gebiet gebiet, WahlartErgebnisart ergebnisart,
      BooleanSupplier waehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlConfigured) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiWahlartRelevant(gebiet.getWahl(), ergebnisart) && isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietsartRelevant(gebiet)
        && waehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlConfigured.getAsBoolean();
  }

  // ELECT-14098
  public boolean isWaehlerMitWahlscheinInUrnenwahlBeiWahlartRelevant(Wahl wahl, WahlartErgebnisart ergebnisart) {
    if (wahl != null) {
      return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiWahlartRelevant(wahl, ergebnisart) || isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiWahlartRelevant(wahl, ergebnisart);
    }

    return true;
  }

  /**
   * @deprecated Nur zur Anzeige "potentieller" Plausis verwenden, da Wahldurchfuehrungsgebiet#isBriefwahlInUrneErfassenInGebietOderDarunter hier nicht bekannt ist!
   */
  @Deprecated
  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiStimmeingabeRelevant(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiWahlartRelevant(wahl, ergebnisart) && isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietsartRelevant(gebietsart);
  }

  /**
   * @deprecated Nur zur Anzeige "potentieller" Plausis verwenden, da Wahldurchfuehrungsgebiet#isBriefwahlInUrneErfassenInGebietOderDarunter hier nicht bekannt ist!
   */
  @Deprecated
  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiStimmeingabeZuPlausibilisieren(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiWahlartRelevant(wahl, ergebnisart) && isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietsartRelevant(gebietsart);
  }

  /**
   * @deprecated TODO ELECT-9319, Information muss aus einem neuen, noch zu definierenden Enum-Wert an der Wahlart kommen! Bspw. "Wähler mit Wahlschein" || "Wähler mit Wahlschein + davon Briefwähler
   *             (in Urnenwahllokal erfasst)" || "Wähler mit Wahlschein + Urnenwähler mit Wahlschein + Briefwähler (in Urnenwahllokal erfasst)" || "Urnenwähler mit Wahlschein + Briefwähler (in
   *             Urnenwahllokal erfasst)"...
   *
   * @see UmgangBWerteKonfiguration#isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiWahlartRelevant
   * @see AbstractStimmeingabeKopfzahlModelBuilder#isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlCreated
   */
  @Deprecated
  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiWahlartRelevant(Wahl wahl, WahlartErgebnisart ergebnisart) {
    return false;
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietsartRelevant(Gebiet gebiet) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietsartRelevant(findStimmeingabePlausiGebietsart(gebiet));
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietsartRelevant(Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return !isBriefwahlbezirk(gebietsart);
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietRelevantAndConfigured(Gebiet gebiet, WahlartErgebnisart ergebnisart,
      BooleanSupplier waehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlConfigured) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiWahlartRelevant(gebiet.getWahl(), ergebnisart) && isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietsartRelevant(gebiet)
        && waehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlConfigured.getAsBoolean();
  }

  /**
   * @deprecated Nur zur Anzeige "potentieller" Plausis verwenden, da Wahldurchfuehrungsgebiet#isBriefwahlInUrneErfassenInGebietOderDarunter hier nicht bekannt ist!
   */
  @Deprecated
  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiStimmeingabeRelevant(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiWahlartRelevant(wahl, ergebnisart) && isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietsartRelevant(gebietsart);
  }

  /**
   * @deprecated Nur zur Anzeige "potentieller" Plausis verwenden, da Wahldurchfuehrungsgebiet#isBriefwahlInUrneErfassenInGebietOderDarunter hier nicht bekannt ist!
   */
  @Deprecated
  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiStimmeingabeZuPlausibilisieren(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiWahlartRelevant(wahl, ergebnisart) && isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietsartRelevant(gebietsart);
  }

  /**
   * @deprecated TODO ELECT-9319, Information muss aus einem neuen, noch zu definierenden Enum-Wert an der Wahlart kommen! Bspw. "Wähler mit Wahlschein" || "Wähler mit Wahlschein + davon Briefwähler
   *             (in Urnenwahllokal erfasst)" || "Wähler mit Wahlschein + Urnenwähler mit Wahlschein + Briefwähler (in Urnenwahllokal erfasst)" || "Urnenwähler mit Wahlschein + Briefwähler (in
   *             Urnenwahllokal erfasst)"...
   *
   * @see UmgangBWerteKonfiguration#isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiWahlartRelevant
   */
  @Deprecated
  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiWahlartRelevant(Wahl wahl, WahlartErgebnisart ergebnisart) {
    return true; // LEGACY!
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietsartRelevant(Gebiet gebiet) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietsartRelevant(findStimmeingabePlausiGebietsart(gebiet));
  }

  public boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietsartRelevant(Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return !isBriefwahlbezirk(gebietsart);
  }

  public boolean isWaehlerGesamtBeiStimmeingabeRelevant(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerGesamtBeiGebietsartRelevant(gebietsart) && getUmgangWaehlerGesamt(wahl, ergebnisart).isBWertBeiStimmeingabeZuErfassenOderAnzuzeigen();
  }

  public boolean isWaehlerGesamtBeiStimmeingabeZuPlausibilisieren(Wahl wahl, WahlartErgebnisart ergebnisart, Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return isWaehlerGesamtBeiGebietsartRelevant(gebietsart) && getUmgangWaehlerGesamt(wahl, ergebnisart).isBWertBeiStimmeingabeZuErfassen();
  }

  public boolean isWaehlerGesamtBeiGebietsartRelevant(Gebiet gebiet) {
    return isWaehlerGesamtBeiGebietsartRelevant(findStimmeingabePlausiGebietsart(gebiet));
  }

  public boolean isWaehlerGesamtBeiGebietsartRelevant(Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return true;
  }

  protected boolean isBriefwahlbezirk(Optional<StimmeingabePlausiGebietsart> gebietsart) {
    return gebietsart.map(StimmeingabePlausiGebietsart::isBriefwahlbezirk).orElse(false);
  }

  protected Optional<StimmeingabePlausiGebietsart> findStimmeingabePlausiGebietsart(Gebiet gebiet) {
    return Optional.empty();
  }
}
