
package de.ivu.elect.presentation.stimmeingabe;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.stimmeingabe.control.DaoStimmeingabeKopfzahlKonfigurationContext;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeKopfzahlKonfiguration;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeKopfzahlKonfigurationContext;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahl;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahlErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahlMeta;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahlTableLayout;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiTyp;
import de.ivu.elect.business.wahl.boundary.WahlSessionSelect;


import static org.apache.commons.lang3.StringUtils.isBlank;

public abstract class AbstractStimmeingabeKopfzahlErfassenView extends AbstractStimmeingabeErfassenView implements StimmeingabeErfassenViewHavingStimmeingabenKopfzahl {

  private static final long serialVersionUID = 1L;

  @Inject
  protected transient StimmeingabeKopfzahlMeta stimmeingabeKopfzahlMeta;

  @Inject
  protected transient WahlSessionSelect wahlSessionSelect;

  @Inject
  protected transient StimmeingabeKopfzahlKonfiguration stimmeingabeKopfzahlKonfiguration;

  protected AbstractStimmeingabeKopfzahlErfassenView() {
    super();
  }

  @Override
  protected String checkPreConditionInternal() {
    // nichts zu tun
    return null;
  }

  @Override
  public void preRenderView() {
    super.preRenderView();

    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getGebiet();

    if (isLetzterErgebniseingangAktuellVorhandenHavingStatusGueltig()) {
      if (isErfassungGesperrt() && !isAnzahlenWahlberechtigteHinterlegtMatchAnzahlenWahlberechtigteAngezeigt(wahldurchfuehrungsgebiet)) {
        addMessageFailedCheckAnzahlenWahlberechtigteHinterlegtMatchAnzahlenWahlberechtigteAngezeigt();
      }
    } else {
      if (!isPruefkennzeichenWahlberechtigteHinterlegtFallsBenoetigt(wahldurchfuehrungsgebiet)) {
        addMessageFailedCheckPruefkennzeichenWahlberechtigteHinterlegtFallsBenoetigt();
      }

      if (!isAnzahlenWahlberechtigteHinterlegtFallsBenoetigt(wahldurchfuehrungsgebiet)) {
        addMessageFailedCheckAnzahlenWahlberechtigteHinterlegtFallsBenoetigt();
      }
    }
  }

  // ELECTBB-119
  private boolean isPruefkennzeichenWahlberechtigteHinterlegtFallsBenoetigt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    if (getStimmeingabeKopfzahlKonfiguration().isPruefkennzeichenWahlberechtigteImportNotwendig(wahldurchfuehrungsgebiet.getWahldurchfuehrung())) {
      StimmeingabeKopfzahlErfassenModel model = getStimmeingabeKopfzahlErfassenModel();

      return !model.hasPruefkennzeichenWahlberechtigte() || wahldurchfuehrungsgebiet.hasPruefkennzeichen();
    }

    return true;
  }

  protected void addMessageFailedCheckPruefkennzeichenWahlberechtigteHinterlegtFallsBenoetigt() {
    addWarningMessage("stimmeingabe.create.action.pruefkennzeichen.nicht.hinterlegt.message");
  }

  // ELECT-3806
  private boolean isAnzahlenWahlberechtigteHinterlegtFallsBenoetigt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    if (getStimmeingabeKopfzahlKonfiguration().isAnyAnzahlWahlberechtigteImportNotwendig(wahldurchfuehrungsgebiet.getWahldurchfuehrung())) {
      StimmeingabeKopfzahlErfassenModel model = getStimmeingabeKopfzahlErfassenModel();

      return (!model.hasWahlberechtigteOhneWahlschein() || wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteOhneWahlschein())
          && (!model.hasWahlberechtigteMitWahlschein() || wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteMitWahlschein())
          && (!model.hasWahlberechtigteNichtImWaehlerverzeichnis() || wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteNichtImWaehlerverzeichnis())
          && (!model.hasWahlberechtigteGesamt() || wahldurchfuehrungsgebiet.hasAnyAnzahlWahlberechtigte());
    }

    return true;
  }

  protected void addMessageFailedCheckAnzahlenWahlberechtigteHinterlegtFallsBenoetigt() {
    addWarningMessage("stimmeingabe.create.action.anzahl.wahlberechtigter.nicht.hinterlegt.message");
  }

  // ELECT-7815
  private boolean isAnzahlenWahlberechtigteHinterlegtMatchAnzahlenWahlberechtigteAngezeigt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    StimmeingabeKopfzahlErfassenModel model = getStimmeingabeKopfzahlErfassenModel();

    return (!model.hasWahlberechtigteOhneWahlschein() || !wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteOhneWahlschein()
        || hasAnzahlEingabeAndMatchesAnzahlExpected(model.getWahlberechtigteOhneWahlschein()))
        && (!model.hasWahlberechtigteMitWahlschein() || !wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteMitWahlschein()
            || hasAnzahlEingabeAndMatchesAnzahlExpected(model.getWahlberechtigteMitWahlschein()))
        && (!model.hasWahlberechtigteNichtImWaehlerverzeichnis() || !wahldurchfuehrungsgebiet.hasAnzahlWahlberechtigteNichtImWaehlerverzeichnis()
            || hasAnzahlEingabeAndMatchesAnzahlExpected(model.getWahlberechtigteNichtImWaehlerverzeichnis()));
  }

  protected void addMessageFailedCheckAnzahlenWahlberechtigteHinterlegtMatchAnzahlenWahlberechtigteAngezeigt() {
    addWarningMessage("stimmeingabe.create.action.anzahl.wahlberechtigter.abweichend.message");
  }

  protected StimmeingabeKopfzahlKonfiguration getStimmeingabeKopfzahlKonfiguration() {
    return stimmeingabeKopfzahlKonfiguration;
  }

  @Override
  protected void gebietWechselt(Gebiet neuesGebiet) {
    reset();

    if (neuesGebiet != null && wahlSessionSelect.isSelected() && isApplicable(wahlSessionSelect.getEntity())) { // ELECTDD-404 Abmeldung und nur, wenn der View für die aktuelle Wahl
      redirectToStimmeingabeErfassenView();
    }
  }

  @Override
  protected Collection<Stimmeingabe> getStimmeingabenFuerDiesenView() {
    Collection<Stimmeingabe> result = new HashSet<>();

    for (StimmeingabeKopfzahl stimmeingabeKopfzahl : getStimmeingabenKopfzahl()) {
      result.add(stimmeingabeKopfzahl.getStimmeingabe());
    }

    return result;
  }

  @Override
  public StimmeingabeKopfzahlTableLayout getStimmeingabenKopfzahlTableLayout() {
    return new StimmeingabeKopfzahlTableLayout( //
        isErfassungGesperrt(), //

        getStimmeingabeErfassenUmgangKennungDefault(), //
        stimmeingabeKopfzahlMeta.getStimmeingabeKennungEinzahlMessage(), //
        stimmeingabeKopfzahlMeta.getStimmeingabeKennungEinzahlMessage(), //

        true, //
        stimmeingabeKopfzahlMeta.getStimmeingabeBezeichnungEinzahlMessage(), //

        true, //
        getStimmeingabenKopfzahlBezeichnung(), //

        isStimmeingabenKopfzahlVergleichRendered(), //
        getStimmeingabenKopfzahlVergleichBezeichnung() //
    );
  }

  @Override
  public List<StimmeingabeKopfzahl> getStimmeingabenKopfzahl() {
    return getStimmeingabeKopfzahlErfassenModelBuilder().layoutStimmeingabenKopfzahl(getStimmeingabeKopfzahlErfassenModel());
  }

  protected boolean isKopfzahlStimmeingabe(Stimmeingabe stimmeingabe) {
    if (stimmeingabe != null) {
      for (StimmeingabeKopfzahl stimmeingabeKopfzahl : getStimmeingabenKopfzahl()) {
        if (stimmeingabeKopfzahl.hasTk(stimmeingabe.getTk())) {
          return true;
        }
      }
    }

    return false;
  }

  @Override
  protected String getStimmeingabeBezug(Stimmeingabe stimmeingabe) {
    String result = getStimmeingabeKopfzahlErfassenLabelGenerator().getKopfzahlStimmeingabeKennung(stimmeingabe, getGruppenImGebiet());

    if (isBlank(result) && stimmeingabe.isPruefkennzeichenEingabe()) {
      return getStimmeingabeKopfzahlErfassenLabelGenerator().getKopfzahlStimmeingabeBezeichnung(stimmeingabe, getGruppenImGebiet());
    }

    return result;
  }

  @Override
  public void checkStimmeingabeKopfzahlAnzahlExpected(StimmeingabeKopfzahl stimmeingabeKopfzahl) {
    if (isStimmeingabeKopfzahlWahlberechtigte(stimmeingabeKopfzahl)) {
      String stimmeingabeBezeichnung = stimmeingabeKopfzahl.getStimmeingabeBezeichnung();

      String stimmeingabeSofortpruefungFehlermeldungMsgKeyPrefix = "StimmeingabePlausiTyp.";
      String stimmeingabeSofortpruefungFehlermeldungMsgKeySuffix = ".hinweis";

      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getGebiet();
      Ergebniseingangsart ergebniseingangsart = getErgebniseingangsart();

      // ELECTBB-119
      if (Stimmenart.PRUEFKENNZEICHEN.equals(stimmeingabeKopfzahl.getStimmenart()) && getStimmeingabeKopfzahlKonfiguration()
          .isPruefkennzeichenWahlberechtigteBeiStimmeingabeZuPlausibilisierenGegenPruefkennzeichenWahlberechtigteAmGebiet(wahldurchfuehrungsgebiet, ergebniseingangsart)
          && !hasAnzahlEingabeAndMatchesAnzahlEingabeExpected(stimmeingabeKopfzahl)) {
        String stimmeingabeSofortpruefungFehlermeldungMsgKey = stimmeingabeSofortpruefungFehlermeldungMsgKeyPrefix
            + StimmeingabePlausiTyp.PRUEFKENNZEICHEN_WAHLBERECHTIGTE_GLEICH_PRUEFKENNZEICHEN_WAHLBERECHTIGTE_AM_GEBIET.name() + stimmeingabeSofortpruefungFehlermeldungMsgKeySuffix;

        addErrorMessage(stimmeingabeSofortpruefungFehlermeldungMsgKey, stimmeingabeBezeichnung);
      }

      StimmeingabeKopfzahlKonfigurationContext context  = new DaoStimmeingabeKopfzahlKonfigurationContext(wahldurchfuehrungsgebietExtendedDAO);
      // ELECT-1990, eingegebene A-Werte sofort mit den importierten Werten vergleichen.
      if (Stimmenart.OHNE_WAHLSCHEIN.equals(stimmeingabeKopfzahl.getStimmenart()) && getStimmeingabeKopfzahlKonfiguration()
          .isWahlberechtigteOhneWahlscheinBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteOhneWahlscheinAmGebiet(wahldurchfuehrungsgebiet,
              ergebniseingangsart, context)
          && !hasAnzahlEingabeAndMatchesAnzahlExpected(stimmeingabeKopfzahl)) {
        String stimmeingabeSofortpruefungFehlermeldungMsgKey = stimmeingabeSofortpruefungFehlermeldungMsgKeyPrefix
            + StimmeingabePlausiTyp.WAHLBERECHTIGTE_OHNE_WAHLSCHEIN_ANZAHL_GLEICH_ANZAHL_AM_GEBIET.name() + stimmeingabeSofortpruefungFehlermeldungMsgKeySuffix;

        addErrorMessage(stimmeingabeSofortpruefungFehlermeldungMsgKey, stimmeingabeBezeichnung);
      }

      if (Stimmenart.MIT_WAHLSCHEIN.equals(stimmeingabeKopfzahl.getStimmenart()) && getStimmeingabeKopfzahlKonfiguration()
          .isWahlberechtigteMitWahlscheinBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteMitWahlscheinAmGebiet(wahldurchfuehrungsgebiet,
              ergebniseingangsart, context)
          && !hasAnzahlEingabeAndMatchesAnzahlExpected(stimmeingabeKopfzahl)) {
        String stimmeingabeSofortpruefungFehlermeldungMsgKey = stimmeingabeSofortpruefungFehlermeldungMsgKeyPrefix
            + StimmeingabePlausiTyp.WAHLBERECHTIGTE_MIT_WAHLSCHEIN_ANZAHL_GLEICH_ANZAHL_AM_GEBIET.name() + stimmeingabeSofortpruefungFehlermeldungMsgKeySuffix;

        addErrorMessage(stimmeingabeSofortpruefungFehlermeldungMsgKey, stimmeingabeBezeichnung);
      }

      if (Stimmenart.NICHT_IM_WAEHLERVERZEICHNIS.equals(stimmeingabeKopfzahl.getStimmenart()) && getStimmeingabeKopfzahlKonfiguration()
          .isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteNichtImWaehlerverzeichnisAmGebiet(wahldurchfuehrungsgebiet, ergebniseingangsart, context)
          && !hasAnzahlEingabeAndMatchesAnzahlExpected(stimmeingabeKopfzahl)) {
        String stimmeingabeSofortpruefungFehlermeldungMsgKey = stimmeingabeSofortpruefungFehlermeldungMsgKeyPrefix
            + StimmeingabePlausiTyp.WAHLBERECHTIGTE_NICHT_IM_WAEHLERVERZEICHNIS_ANZAHL_GLEICH_ANZAHL_AM_GEBIET.name() + stimmeingabeSofortpruefungFehlermeldungMsgKeySuffix;

        addErrorMessage(stimmeingabeSofortpruefungFehlermeldungMsgKey, stimmeingabeBezeichnung);
      }

      if (Stimmenart.GESAMT.equals(stimmeingabeKopfzahl.getStimmenart())
          && getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteGesamtBeiStimmeingabeZuPlausibilisierenGegenWahlberechtigteGesamtAmGebiet(wahldurchfuehrungsgebiet, ergebniseingangsart, context)
          && !hasAnzahlEingabeAndMatchesAnzahlExpected(stimmeingabeKopfzahl)) {
        String stimmeingabeSofortpruefungFehlermeldungMsgKey = stimmeingabeSofortpruefungFehlermeldungMsgKeyPrefix + StimmeingabePlausiTyp.WAHLBERECHTIGTE_GESAMT_ANZAHL_GLEICH_ANZAHL_AM_GEBIET.name()
            + stimmeingabeSofortpruefungFehlermeldungMsgKeySuffix;

        addErrorMessage(stimmeingabeSofortpruefungFehlermeldungMsgKey, stimmeingabeBezeichnung);
      }
    }
  }

  private boolean isStimmeingabeKopfzahlWahlberechtigte(StimmeingabeKopfzahl stimmeingabeKopfzahl) {
    return stimmeingabeKopfzahl != null && stimmeingabeKopfzahl.getStimmeingabe() != null && isStimmeingabeKopfzahlWahlberechtigte(stimmeingabeKopfzahl.getStimmeingabe());
  }

  protected boolean hasAnzahlEingabeAndMatchesAnzahlEingabeExpected(StimmeingabeKopfzahl stimmeingabeKopfzahl) {
    if (stimmeingabeKopfzahl.hasAnzahlEingabe() && stimmeingabeKopfzahl.hasAnzahlEingabeExpected()) {
      return stimmeingabeKopfzahl.getAnzahlEingabe().equals(stimmeingabeKopfzahl.getAnzahlEingabeExpected());
    }

    return true;
  }

  protected boolean hasAnzahlEingabeAndMatchesAnzahlExpected(StimmeingabeKopfzahl stimmeingabeKopfzahl) {
    if (stimmeingabeKopfzahl.hasAnzahlEingabe() && stimmeingabeKopfzahl.hasAnzahlExpected()) {
      return stimmeingabeKopfzahl.getAnzahl().equals(stimmeingabeKopfzahl.getAnzahlExpected());
    }

    return true;
  }

  protected String getStimmeingabenKopfzahlBezeichnung() {
    return stimmeingabeKopfzahlMeta.getStimmeingabeAnzahlEingabeEinzahlMessage();
  }

  protected boolean isStimmeingabenKopfzahlVergleichRendered() {
    return isVergleichErgebniseingangVorhanden();
  }

  protected String getStimmeingabenKopfzahlVergleichBezeichnung() {
    return getMessage("stimmeingabe.erfassen.vergleich.ergebniseingang.bezeichnung");
  }
}
