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

import java.util.Collection;

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.NlSystemGruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppeEigenschaften;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingangKonfiguration;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.NlStimmeingabeKopfzahlErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmenErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahlErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiFehlerart;

public abstract class AbstractNlKopfzahlPlausiBuilder extends AbstractKopfzahlPlausiBuilder {

  @Inject
  private transient SystemGruppeEigenschaften systemGruppeEigenschaften;

  @Inject
  private transient ErgebniseingangKonfiguration ergebniseingangKonfiguration;

  public AbstractNlKopfzahlPlausiBuilder() {
    super();
  }

  protected ErgebniseingangKonfiguration getErgebniseingangKonfiguration() {
    return ergebniseingangKonfiguration;
  }

  public abstract boolean containsAnyStimmeingabefehlerEndstufeSpezifisch(Ergebniseingang ergebniseingang);

  public void fillKopfzahlPlausis(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel) {
    fillKopfzahlPlausisWahlberechtigte(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel);

    fillKopfzahlPlausisWaehler(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel);

    fillKopfzahlPlausisStimmzettel(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel);

    fillKopfzahlPlausisUnspezifisch(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel, findKopfzahlPlausiUnspezifischStimmeingabe(context.getErgebniseingang()));

    logStimmeingabePlausis(context, kopfzahlErfassenModel.getAllStimmeingaben());
  }

  protected void fillKopfzahlPlausisWahlberechtigte(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel) {
    fillPruefkennzeichenWahlberechtigtePlausis(kopfzahlErfassenModel, context);

    fillUrnenwahlbezirkWahlberechtigteAnzahlGleichWahlberechtigteAnzahlAmGebietPlausis(kopfzahlErfassenModel, context);

    addUrnenwahlbezirkWahlberechtigteGesamtGroesserNullPlausi(kopfzahlErfassenModel, context, StimmeingabePlausiFehlerart.WARNING); // ELECTVAPP-37, #13, Anzahl Wahlberechtigte > 0
  }

  protected void fillKopfzahlPlausisWaehler(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel) {
    fillUrnenwahlbezirkKopfzahlSummenWaehlerPlausis(kopfzahlErfassenModel, context, StimmeingabePlausiFehlerart.WARNING); // ELECTVAPP-37, #2, D = A + B + C
  }

  protected void fillKopfzahlPlausisStimmzettel(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel) {
    fillKopfzahlPlausisStimmzettelEingenommen(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel);
    fillKopfzahlPlausisStimmzettelEingenommenAbweichend(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel);
  }

  protected void fillKopfzahlPlausisStimmzettelEingenommen(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel) {
    kopfzahlErfassenModel.getStimmzettelEingenommenGesamtStimmeingabe().ifPresent(stimmzetteleingabeStimmzettelEingenommenGesamt -> {
      addPlausi(stimmzetteleingabeStimmzettelEingenommenGesamt, // ELECTVAPP-37, #7, Anzahl Eingenommene Stimmzettel > 0
          new StimmeingabeAnzahlGroesserAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_STIMMZETTEL_GESAMT,
              findStimmeingabePlausiKonfigurationOrDefaults(context, AbstractNlStimmeingabePlausiDefinitions.STIMMZETTEL_EINGENOMMEN_GESAMT_ANZAHL_GROESSER_NULL, StimmeingabePlausiFehlerart.WARNING),
              getMessages()),
          context);
    });
  }

  protected void fillKopfzahlPlausisStimmzettelEingenommenAbweichend(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel) {
    kopfzahlErfassenModel.getStimmzettelEingenommenGesamtStimmeingabe().ifPresent(stimmzetteleingabeStimmzettelEingenommenGesamt -> {
      Collection<Stimmeingabe> stimmeingabenWaehlerGesamt = kopfzahlErfassenModel.getWaehlerGesamtStimmeingaben();

      Stimmeingabe stimmzetteleingabeStimmzettelEingenommenAbweichendZuwenig = kopfzahlErfassenModel.getStimmzettelEingenommenAbweichendZuwenigStimmeingabe().orElse(null);
      Stimmeingabe stimmzetteleingabeStimmzettelEingenommenAbweichendZuviel = kopfzahlErfassenModel.getStimmzettelEingenommenAbweichendZuvielStimmeingabe().orElse(null);
      Collection<Stimmeingabe> stimmzetteleingabenStimmzettelEingenommenAbweichendGrund = kopfzahlErfassenModel.getStimmzettelEingenommenAbweichendGrundStimmeingaben();

      addPlausi(stimmzetteleingabeStimmzettelEingenommenGesamt, // ELECTVAPP-37, #8
          new NlStimmzettelEingenommenGesamtAnzahlGleichWaehlerGesamtAnzahlPlausi(stimmeingabenWaehlerGesamt, stimmzetteleingabeStimmzettelEingenommenAbweichendZuwenig,
              stimmzetteleingabeStimmzettelEingenommenAbweichendZuviel, stimmzetteleingabenStimmzettelEingenommenAbweichendGrund, findStimmeingabePlausiKonfigurationOrDefaults(context,
                  AbstractNlStimmeingabePlausiDefinitions.STIMMZETTEL_EINGENOMMEN_GESAMT_ANZAHL_GLEICH_WAEHLER_GESAMT_ANZAHL, StimmeingabePlausiFehlerart.WARNING),
              getMessages()),
          context);

      addPlausi(stimmzetteleingabeStimmzettelEingenommenGesamt, // ELECTVAPP-93
          new NlStimmzettelEingenommenGesamtAnzahlUngleichWaehlerGesamtAnzahlPlausi(stimmeingabenWaehlerGesamt, stimmzetteleingabeStimmzettelEingenommenAbweichendZuwenig,
              stimmzetteleingabeStimmzettelEingenommenAbweichendZuviel, stimmzetteleingabenStimmzettelEingenommenAbweichendGrund, findStimmeingabePlausiKonfigurationOrDefaults(context,
                  AbstractNlStimmeingabePlausiDefinitions.STIMMZETTEL_EINGENOMMEN_GESAMT_ANZAHL_UNGLEICH_WAEHLER_GESAMT_ANZAHL, StimmeingabePlausiFehlerart.WARNING),
              getMessages()),
          context);

      if (stimmzetteleingabeStimmzettelEingenommenAbweichendZuviel != null) {
        addPlausi(stimmzetteleingabeStimmzettelEingenommenAbweichendZuviel, // ELECTVAPP-37, #10
            new NlStimmzettelEingenommenAbweichendZuvielAnzahlGleichStimmzettelEingenommenGesamtAnzahlMinusWaehlerGesamtAnzahlPlausi(stimmeingabenWaehlerGesamt,
                stimmzetteleingabeStimmzettelEingenommenGesamt,
                findStimmeingabePlausiKonfigurationOrDefaults(context,
                    AbstractNlStimmeingabePlausiDefinitions.STIMMZETTEL_EINGENOMMEN_ABWEICHEND_ZUVIEL_ANZAHL_GLEICH_STIMMZETTEL_EINGENOMMEN_GESAMT_ANZAHL_MINUS_WAEHLER_GESAMT_ANZAHL,
                    StimmeingabePlausiFehlerart.WARNING),
                getMessages()),
            context);
      }

      if (stimmzetteleingabeStimmzettelEingenommenAbweichendZuwenig != null) {
        addPlausi(stimmzetteleingabeStimmzettelEingenommenAbweichendZuwenig, // ELECTVAPP-37, #11
            new NlStimmzettelEingenommenAbweichendZuwenigAnzahlGleichWaehlerGesamtAnzahlMinusStimmzettelEingenommenGesamtAnzahlPlausi(stimmeingabenWaehlerGesamt,
                stimmzetteleingabeStimmzettelEingenommenGesamt,
                findStimmeingabePlausiKonfigurationOrDefaults(context,
                    AbstractNlStimmeingabePlausiDefinitions.STIMMZETTEL_EINGENOMMEN_ABWEICHEND_ZUWENIG_ANZAHL_GLEICH_WAEHLER_GESAMT_ANZAHL_MINUS_STIMMZETTEL_EINGENOMMEN_GESAMT_ANZAHL,
                    StimmeingabePlausiFehlerart.WARNING),
                getMessages()),
            context);
      }
    });
  }

  protected void fillKopfzahlPlausisUnspezifisch(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel, Stimmeingabe kopfzahlPlausiUnspezifischStimmeingabe) {
    // To be overridden...
  }

  @Override
  protected void fillKopfzahlPlausisWahlartSpezifisch(StimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel, StimmeingabePlausiBuilderContext context) {
    // Cf. AbstractNlKopfzahlPlausiBuilder#fillKopfzahlPlausis...
  }

  /**
   * @return Liefert eine EXISTIERENDE Kopfzahl-Stimmeingabe, um an diese einen allgemeinen Stimmeingabefehler zu hängen, der nicht zwangsläufig mit dem Wert der gelieferten Stimmeingabe zu tun hat,
   *         aber eben an IRGENDEINER Stimmeingabe hängen muss. Diese Logik ist notwendig, da nicht in jeder Wahlart und vorallem nicht auf jeder Ebene (PSB, HSB, CSB) dieselben Kopfzahl-Stimmeingaben
   *         existieren!
   */
  public Stimmeingabe findKopfzahlPlausiUnspezifischStimmeingabe(Ergebniseingang ergebniseingang) {
    Collection<Stimmeingabe> stimmeingaben = ergebniseingang.getStimmeingaben();

    if (stimmeingaben.isEmpty()) {
      throw new IllegalStateException("Ergebniseingang OHNE Stimmeingaben!");
    }

    Stimmeingabe result = null;

    for (Stimmeingabe stimmeingabe : stimmeingaben) {
      Gruppe gruppe = stimmeingabe.getGruppe();
      Stimmenart stimmenart = stimmeingabe.getStimmenart();

      if (NlSystemGruppe.WAHLBERECHTIGTE.equalsSystemGruppe(gruppe, systemGruppeEigenschaften)) {
        if (Stimmenart.GESAMT.equals(stimmenart)) {
          result = stimmeingabe;
        }
      } else if (NlSystemGruppe.WAEHLER.equalsSystemGruppe(gruppe, systemGruppeEigenschaften)) {
        if (Stimmenart.GESAMT.equals(stimmenart)) {
          result = stimmeingabe;
        }
      } else if (NlSystemGruppe.STIMMZETTEL_EINGENOMMEN.equalsSystemGruppe(gruppe, systemGruppeEigenschaften)) {
        if (Stimmenart.GESAMT_EINGENOMMEN.equals(stimmenart)) {
          result = stimmeingabe;
        }
      } else if (NlSystemGruppe.GUELTIGE.equalsSystemGruppe(gruppe, systemGruppeEigenschaften)) {
        if (Stimmenart.LISTENSTIMME.equals(stimmenart) || Stimmenart.REFERENDUM_STIMME.equals(stimmenart)) {
          result = stimmeingabe;
        }
      }

      if (result != null) {
        break;
      }
    }

    if (result == null) {
      result = stimmeingaben.iterator().next();
    }

    return result;
  }
}
