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

import static de.ivu.elect.jee.entity.EntityUtil.castTo;
import static java.util.Arrays.asList;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.SystemGruppe;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.stimmeingabe.boundary.AbstractStimmeingabeReferendumVorlageStimmenModelBuilder;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiFehlerart;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiTyp;
import de.ivu.elect.business.stimmeingabe.entity.ZwischensummeEingabe;
import de.ivu.elect.jee.entity.tk.EntityTK;

/*
 * TODO ELECT-6203, diese Klasse wurde von MUC kopiert!
 * TODO Die Models nutzen, die am Ergebniseingang hängen
 */
public abstract class AbstractReferendumVorlageStimmeingabePlausiBuilder extends AbstractIterierenderStimmeingabePlausiBuilder {

  private static final long serialVersionUID = 1L;

  protected AbstractReferendumVorlageStimmeingabePlausiBuilder() {
    super();
  }

  @Override
  protected void fillStimmeingabePlausis(Ergebniseingang ergebniseingang, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingangsart ergebniseingangsart,
      Set<Stimmeingabe> stimmeingabenZuPlausibilisieren, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenPerGruppeImGebiet,
      Map<Stimmenart, Long> anzahlWahlberechtigteAmGebietPerStimmenart, Map<EntityTK, Stimmeingabe> stimmeingabeVorlaeufigPerTK) {
    double anteilWahlberechtigteMitWahlschein = 0.90D;

    fillStimmeingabePlausis(ergebniseingang, wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabenZuPlausibilisieren, stimmeingabenPerSystemGruppe, stimmeingabenPerGruppeImGebiet,
        anzahlWahlberechtigteAmGebietPerStimmenart, stimmeingabeVorlaeufigPerTK, anteilWahlberechtigteMitWahlschein);
  }

  @Override
  protected void fillWaehlerGesamtFuerNichtBriefwahlPlausis(Stimmeingabe stimmeingabeWaehler, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe,
      Ergebniseingangsart ergebniseingangsart) {
    // ELECT-14590, Legacy-Defaults...
    double waehlerGesamtZuWahlberechtigteGesamtAnteilMin = 0.10D;
    double waehlerGesamtZuWahlberechtigteGesamtAnteilMax = 0.95D;

    fillWaehlerGesamtFuerNichtBriefwahlPlausis(stimmeingabeWaehler, stimmeingabenPerSystemGruppe, ergebniseingangsart, waehlerGesamtZuWahlberechtigteGesamtAnteilMin,
        waehlerGesamtZuWahlberechtigteGesamtAnteilMax, StimmeingabePlausiFehlerart.UNBEKANNT);
  }

  @Override
  protected void fillWahlSpezifischeSystemGruppeStimmeingabePlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenBySystemGruppe,
      Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenByGruppeImGebiet, Map<GruppeImGebiet, Stimmeingabe> gueltigeListenstimmenAllerParteien, Gebiet gebiet, Gebietsart gebietsart,
      Ergebniseingangsart ergebniseingangsart) {
    if (stimmeingabe.isSystemGruppenStimmeingabe()) {
      fillWahlSpezifischeWaehlerGesamtPlausis(stimmeingabe, stimmeingabenByGruppeImGebiet);

      // ELECTMUC-853
      if (!isSonderstimmbezirk(gebiet)) {
        fillWahlSpezifischeWaehlerGesamtFuerNichtBriefwahlPlausis(stimmeingabe, stimmeingabenBySystemGruppe, gebietsart);
      }

      fillWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlPlausis(stimmeingabe, stimmeingabenBySystemGruppe);
    }
  }

  private void fillWahlSpezifischeWaehlerGesamtPlausis(Stimmeingabe stimmeingabeWaehlerGesamt, Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenPerGruppeImGebiet) {
    if (isStimmeingabeWaehlerGesamt(stimmeingabeWaehlerGesamt)) {
      for (GruppeImGebiet gruppeImGebiet : stimmeingabenPerGruppeImGebiet.keySet()) {
        if (gruppeImGebiet.isReferendumVorlage()) {
          List<Stimmeingabe> referendumVorlageStimmeingaben = stimmeingabenPerGruppeImGebiet.get(gruppeImGebiet);

          // NOTE ELECTDD-369, dass es keine Stimmeingabe für 'Abgegebene Stimmen insgesamt' mehr gibt. Deshalb erhält jede Referendum
          // Vorlage eine neue Plausi, die die Regel 'Gültige Stimmen (D) + Ungültige Stimmen (C) = Wähler/Abstimmende Personen (B)' überprüft.
          Stimmeingabe zwischensummeEingabeReferendumVorlageGueltige = findZwischensummeAnzahlMitIndex(referendumVorlageStimmeingaben,
              AbstractStimmeingabeReferendumVorlageStimmenModelBuilder.GRUPPE_STIMMEN_GUELTIGE_GESAMT_ZWISCHENSUMME_INDEX);
          Stimmeingabe zwischensummeEingabeReferendumVorlageUngueltige = findZwischensummeAnzahlMitIndex(referendumVorlageStimmeingaben,
              AbstractStimmeingabeReferendumVorlageStimmenModelBuilder.GRUPPE_STIMMEN_UNGUELTIGE_GESAMT_ZWISCHENSUMME_INDEX);

          if (isStimmeingabePlausiKontextGueltig(asList(zwischensummeEingabeReferendumVorlageGueltige, zwischensummeEingabeReferendumVorlageUngueltige))) {
            stimmeingabeWaehlerGesamt.addStimmeingabePlausi(new ReferendumWaehlerGesamtAnzahlGleichVorlageStimmenGueltigeZwischensummePlusVorlageStimmenUngueltigeZwischensummePlausi(
                zwischensummeEingabeReferendumVorlageGueltige, zwischensummeEingabeReferendumVorlageUngueltige,
                findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerGesamt,
                    StimmeingabePlausiTyp.WAEHLER_GESAMT_ANZAHL_GLEICH_GUELTIGE_REFERENDUM_STIMMEN_ANZAHL_PLUS_UNGUELTIGE_REFERENDUM_STIMMEN_ANZAHL, StimmeingabePlausiFehlerart.ERROR),
                getMessages()));
          }
        }
      }
    }
  }

  protected void fillWahlSpezifischeWaehlerGesamtFuerNichtBriefwahlPlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenBySystemGruppe, Gebietsart gebietsart) {
    if (isStimmeingabeWaehlerGesamt(stimmeingabe) && !gebietsart.isBriefwahlbezirk()) {
      Stimmeingabe stimmeingabeWaehlerOhneWahlschein = findStimmeingabeMitStimmenart(stimmeingabenBySystemGruppe.get(SystemGruppe.WAEHLER), Stimmenart.OHNE_WAHLSCHEIN);
      Stimmeingabe stimmeingabeWaehlerMitWahlschein = findStimmeingabeMitStimmenart(stimmeingabenBySystemGruppe.get(SystemGruppe.WAEHLER), Stimmenart.MIT_WAHLSCHEIN);
      Stimmeingabe stimmeingabeWaehlerGesamt = stimmeingabe;

      if (stimmeingabeWaehlerOhneWahlschein != null && stimmeingabeWaehlerMitWahlschein != null) {
        Stimmeingabe stimmeingabeWaehlerMitVollmacht = findStimmeingabeMitStimmenart(stimmeingabenBySystemGruppe.get(SystemGruppe.WAEHLER), Stimmenart.MIT_VOLLMACHT);

        stimmeingabeWaehlerGesamt.addStimmeingabePlausi( // VE 06 Muss-Fehler
            new WaehlerGesamtAnzahlGleichWaehlerAnzahlenSummePlausi(stimmeingabeWaehlerOhneWahlschein, stimmeingabeWaehlerMitWahlschein, stimmeingabeWaehlerMitVollmacht,
                findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerGesamt, StimmeingabePlausiTyp.WAEHLER_GESAMT_ANZAHL_GLEICH_WAEHLER_ANZAHLEN_SUMME, StimmeingabePlausiFehlerart.ERROR),
                getMessages()));
      }
    }
  }

  @Override
  protected void fillWahlSpezifischeNichtSystemGruppeStimmeingabePlausis(Stimmeingabe stimmeingabe, List<Stimmeingabe> stimmeingabenFuerGruppeImGebiet, Ergebniseingangsart ergebniseingangsart) {
    if (isStimmeingabeZwischensummeReferendumOptionGueltige(stimmeingabe)) {
      Stimmeingabe zwischensummeEingabeReferendumVorlageGueltige = stimmeingabe;

      Collection<Stimmeingabe> referendumOptionStimmen = castTo(zwischensummeEingabeReferendumVorlageGueltige, ZwischensummeEingabe.class).getAbhaengigeStimmeingaben();

      if (isStimmeingabePlausiKontextGueltig(referendumOptionStimmen)) {
        zwischensummeEingabeReferendumVorlageGueltige.addStimmeingabePlausi(new ReferendumVorlageStimmenGueltigeZwischensummeGleichOptionStimmenAnzahlenSummePlausi(referendumOptionStimmen,
            findStimmeingabePlausiKonfigurationOrDefaults(zwischensummeEingabeReferendumVorlageGueltige,
                StimmeingabePlausiTyp.GUELTIGE_REFERENDUM_STIMMEN_ANZAHL_GLEICH_REFERENDUM_OPTION_REFERENDUM_STIMMEN_ANZAHLEN_SUMME, StimmeingabePlausiFehlerart.ERROR),
            getMessages()));
      }

      zwischensummeEingabeReferendumVorlageGueltige.addStimmeingabePlausi(
          new StimmeingabeAnzahlGroesserAnzahlPlausi(StimmeingabeAnzahlReference.REFERENDUM_VORLAGE_REFERENDUM_STIMMEN_ZWISCHENSUMME_GUELTIGE, findStimmeingabePlausiKonfigurationOrDefaults(
              zwischensummeEingabeReferendumVorlageGueltige, StimmeingabePlausiTyp.GUELTIGE_REFERENDUM_STIMMEN_ANZAHL_GROESSER_NULL, StimmeingabePlausiFehlerart.ERROR), getMessages()));
    }
  }

  private boolean isStimmeingabeZwischensummeReferendumOptionGueltige(Stimmeingabe stimmeingabe) {
    return stimmeingabe.isZwischensummeEingabe()
        && AbstractStimmeingabeReferendumVorlageStimmenModelBuilder.GRUPPE_STIMMEN_GUELTIGE_GESAMT_ZWISCHENSUMME_INDEX.equals(castTo(stimmeingabe, ZwischensummeEingabe.class).getSummenIndex());
  }

  private boolean isStimmeingabeZwischensummeReferendumOptionAbgegeben(Stimmeingabe stimmeingabe) {
    return stimmeingabe.isZwischensummeEingabe()
        && AbstractStimmeingabeReferendumVorlageStimmenModelBuilder.GRUPPE_STIMMEN_ABGEGEBEN_GESAMT_ZWISCHENSUMME_INDEX.equals(castTo(stimmeingabe, ZwischensummeEingabe.class).getSummenIndex());
  }

  private void fillWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlPlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenBySystemGruppe) {
    Stimmeingabe stimmeingabeWaehlerMitWahlschein = findStimmeingabeMitStimmenart(stimmeingabenBySystemGruppe.get(SystemGruppe.WAEHLER), Stimmenart.MIT_WAHLSCHEIN);

    if (isStimmeingabePlausiKontextGueltig(asList(stimmeingabeWaehlerMitWahlschein))) {
      if (isStimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl(stimmeingabe)) {
        Stimmeingabe stimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl = stimmeingabe;

        stimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl.addStimmeingabePlausi( // ELECTDD-349, Wähler mit Wahlschein >= Wähler mit Wahlschein aus Briefwahl
            new WaehlerMitWahlscheinAnzahlAusBriefwahlAnzahlKleinerGleichWaehlerMitWahlscheinAnzahlPlausi(stimmeingabeWaehlerMitWahlschein,
                findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl,
                    StimmeingabePlausiTyp.WAEHLER_AUS_BRIEFWAHLBEZIRK_ERFASST_IN_URNENWAHLBEZIRK_ANZAHL_KLEINER_GLEICH_WAEHLER_MIT_WAHLSCHEIN_ANZAHL, StimmeingabePlausiFehlerart.ERROR),
                getMessages()));

        Stimmeingabe stimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl = findStimmeingabeMitStimmenart(stimmeingabenBySystemGruppe.get(SystemGruppe.WAEHLER),
            Stimmenart.ALS_URNENWAHL_IN_URNE);

        if (stimmeingabeWaehlerMitWahlschein != null && isStimmeingabePlausiKontextGueltig(asList(stimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl))) {
          stimmeingabeWaehlerMitWahlschein.addStimmeingabePlausi( // ELECT-13269, Wähler mit Wahlschein =  Wähler mit Wahlschein aus Urnenwahl +  Wähler mit Wahlschein aus Briefwahl
              new WaehlerMitWahlscheinAnzahlGleichWaehlerMitWahlscheinAnzahlenSummePlausi(stimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl,
                  stimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl, findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerMitWahlschein,
                      StimmeingabePlausiTyp.WAEHLER_MIT_WAHLSCHEIN_ANZAHL_GLEICH_WAEHLER_MIT_WAHLSCHEIN_ANZAHLEN_SUMME, StimmeingabePlausiFehlerart.ERROR),
                  getMessages()));
        }
      }
    }
  }
}
