
package de.ivu.elect.presentation.stimmeingabe;

import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;

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

import javax.inject.Inject;
import javax.inject.Named;

import org.apache.deltaspike.core.api.scope.ViewAccessScoped;

import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.security.UseCaseSecure;
import de.ivu.elect.business.stimmeingabe.boundary.NlDefaultListenwahlStimmeingabeErfassenDescription;
import de.ivu.elect.business.stimmeingabe.boundary.NlWasStimmeingabeBoundaryCreate;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeGruppeStimmenErfassenModelBuilder;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingabestufe;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.ErgebniseingangErfasser;
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.StimmeingabeKopfzahlTableLayout;
import de.ivu.elect.business.wahl.entity.NlWahlCustomData;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterStimmeingabeErfassungsmaskeUmgangKennung;
import de.ivu.elect.jee.presentation.dynamicform.AdvancedDynaFormModel;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.View;

@ViewAccessScoped
@Named(NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenView.COMPONENT_NAME)
@View(viewId = NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenView.VIEW_ID, useCase = UseCasePermission.STIMMEINGABE_ANZEIGEN)
public class NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenView extends AbstractStimmeingabeGruppeStimmenErfassenViewListingListenkandidaturStimmen {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "nlDefaultListenwahlStimmeingabeGruppeStimmenErfassenView";

  public static final String VIEW_ID = "/stimmeingabe/nl-default-listenwahl-stimmeingabe-gruppe-stimmen-erfassen.xhtml";

  @Inject
  private transient NlDefaultListenwahlStimmeingabeErfassenDescription defaultListenwahlStimmeingabeErfassenDescription;

  public NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenView() {
    super();
  }

  @Override
  public String getViewId() {
    return VIEW_ID;
  }

  @Override
  public boolean isApplicable(Wahl wahl) {
    StimmeingabeGruppeStimmenErfassenModelBuilder stimmeingabeGruppeStimmenErfassenModelBuilder = getStimmeingabeGruppeStimmenErfassenModelBuilder();

    if (wahl != null && stimmeingabeGruppeStimmenErfassenModelBuilder != null) {
      return stimmeingabeGruppeStimmenErfassenModelBuilder.isApplicable(wahl);
    }

    return false;
  }

  @Override
  protected NlDefaultListenwahlStimmeingabeErfassenDescription getStimmeingabeErfassenDescription() {
    return defaultListenwahlStimmeingabeErfassenDescription;
  }

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

    for (StimmeingabeKopfzahl stimmeingabeKopfzahl : getStimmeingabenKopfzahlOberhalb()) { // ELECTVAPP-1320
      result.add(stimmeingabeKopfzahl.getStimmeingabe());
    }

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

    for (StimmeingabeKopfzahl stimmeingabeKopfzahl : getStimmeingabenKopfzahlUnterhalb()) { // ELECTVAPP-1320
      result.add(stimmeingabeKopfzahl.getStimmeingabe());
    }

    return result;
  }

  @Override
  protected void addMessageFailedCheckAnzahlenWahlberechtigteHinterlegtFallsBenoetigt() {
    // ELECT-10826, KEINEN Hinweis anzeigen...
  }

  @Override
  protected WahlParameterStimmeingabeErfassungsmaskeUmgangKennung getStimmeingabeErfassenUmgangKennungDefault() {
    return WahlParameterStimmeingabeErfassungsmaskeUmgangKennung.VOR_UND_NACH_STIMMEINGABE; // ELECTVAPP-184
  }

  @Override
  public List<StimmeingabeKopfzahl> getStimmeingabenKopfzahl() {
    return getStimmeingabenKopfzahlOberhalb();
  }

  public StimmeingabeKopfzahlTableLayout getStimmeingabenKopfzahlOberhalbTableLayout() {
    return getStimmeingabenKopfzahlTableLayout();
  }

  public List<StimmeingabeKopfzahl> getStimmeingabenKopfzahlOberhalb() {
    return createLayout().collectStimmeingabenKopfzahlOberhalb();
  }

  public StimmeingabeKopfzahlTableLayout getStimmeingabenKopfzahlUnterhalbTableLayout() {
    return getStimmeingabenKopfzahlTableLayout();
  }

  public List<StimmeingabeKopfzahl> getStimmeingabenKopfzahlUnterhalb() {
    return createLayout().collectStimmeingabenKopfzahlUnterhalb();
  }

  public String getStimmeingabenKopfzahlStimmzettelEingenommenAbweichendExpandedLabel() {
    return getMessage("stimmeingabe.erfassen.UEBRIGE.ANZAHL.GESAMT_ABWEICHEND.header");
  }

  // ELECTVAPP-93
  public boolean getStimmeingabenKopfzahlStimmzettelEingenommenAbweichendExpanded() {
    return createLayout().isStimmeingabenKopfzahlStimmzettelEingenommenAbweichendExpanded();
  }

  public void setStimmeingabenKopfzahlStimmzettelEingenommenAbweichendExpanded(boolean stimmeingabenKopfzahlStimmzettelEingenommenAbweichendExpanded) {
    // ELECTVAPP-93, Zustand nicht speichern, um alle wichtigen Eingaben IMMER sichtbar zu machen...
  }

  public boolean isStimmeingabenKopfzahlStimmzettelEingenommenAbweichendErfasst() {
    return createLayout().isStimmeingabenKopfzahlStimmzettelEingenommenAbweichendErfasst();
  }

  public StimmeingabeKopfzahlTableLayout getStimmeingabenKopfzahlStimmzettelEingenommenAbweichendTableLayout() {
    return getStimmeingabenKopfzahlTableLayout();
  }

  public List<StimmeingabeKopfzahl> getStimmeingabenKopfzahlStimmzettelEingenommenAbweichend() {
    return createLayout().collectStimmeingabenKopfzahlStimmzettelEingenommenAbweichend();
  }

  private NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenLayout createLayout() {
    return new NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenLayout(getCustomer(), this);
  }

  @Override
  protected void addMessageAktuellerErgebniseingangErfassungGesperrtByErgebniseingabestufe() {
    if (isErgebniseingangErfasserErfasst()) {
      addWarningMessage("stimmeingabe.erfassen.pruefen.eingabe.auf.gebiet.gesperrt.ergebniseingabestufe.nicht.bearbeitbar." + getNaechsterErgebniseingangErgebniseingabestufe().name(),
          getGebietName());
    }
  }

  @Override
  protected boolean isAktuellerErgebniseingangErgebniseingabestufeLaesstEingabeZu() {
    Ergebniseingabestufe aktuellerErgebniseingangErgebniseingabestufe = getAktuellerErgebniseingangErgebniseingabestufe();

    if (isErgebniseingangErfasserKontrolliert()) {
      if (aktuellerErgebniseingangErgebniseingabestufe.isInErgebnisUebernehmbar()) {
        return true;
      }

      Ergebniseingang letzterErgebniseingangAktuell = getLetzterErgebniseingangAktuell();

      return letzterErgebniseingangAktuell != null && !letzterErgebniseingangAktuell.getErgebniseingangsherkunft().isBenutzerEingabe(); // ELECT-10500
    }

    if (isErgebniseingangErfasserErfasst()) {
      return !aktuellerErgebniseingangErgebniseingabestufe.isInErgebnisUebernehmbar()
          && Objects.equals(aktuellerErgebniseingangErgebniseingabestufe, getNaechsterErgebniseingangErgebniseingabestufe());
    }

    return false;
  }

  private Ergebniseingabestufe getNaechsterErgebniseingangErgebniseingabestufe() {
    Ergebniseingabestufe ergebniseingabestufeZuErfassen = getAktuellerErgebniseingangErgebniseingabestufe();

    Ergebniseingang letzterErgebniseingangErfasst = getLetzterErgebniseingangErfasst();

    if (letzterErgebniseingangErfasst != null) {
      ergebniseingabestufeZuErfassen = letzterErgebniseingangErfasst.getErgebniseingabestufe();

      if (letzterErgebniseingangErfasst.isGueltig()) {
        ergebniseingabestufeZuErfassen = ergebniseingabestufeZuErfassen.getNachfolger();
      }
    }

    return ergebniseingabestufeZuErfassen;
  }

  @Override
  protected Ergebniseingabestufe findAktuellerErgebniseingangErgebniseingabestufe(ErgebniseingangErfasser erfasser, Ergebniseingang letzterErgebniseingangErfasst,
      Map<Ergebniseingabestufe, Ergebniseingang> letzterErgebniseingangErfasstPerErgebniseingabestufe) {
    if (letzterErgebniseingangErfasst != null && isErgebniseingangErfasserKontrolliert()) {
      return letzterErgebniseingangErfasst.getErgebniseingabestufe(); // Letzte überhaupt erfasste Ergebniseingabestufe...
    }

    Ergebniseingang letzterErgebniseingangErfasstVorstufe01 = letzterErgebniseingangErfasstPerErgebniseingabestufe.get(Ergebniseingabestufe.VORSTUFE_01);
    Ergebniseingang letzterErgebniseingangErfasstVorstufe02 = letzterErgebniseingangErfasstPerErgebniseingabestufe.get(Ergebniseingabestufe.VORSTUFE_02);
    Ergebniseingang letzterErgebniseingangErfasstEndstufe = letzterErgebniseingangErfasstPerErgebniseingabestufe.get(Ergebniseingabestufe.ENDSTUFE);

    if (letzterErgebniseingangErfasstVorstufe01 == null && letzterErgebniseingangErfasstVorstufe02 == null) {
      if (letzterErgebniseingangErfasstEndstufe != null) {
        return Ergebniseingabestufe.ENDSTUFE; // Enderfassung importiert -> keine Vorstufen...
      }
    }

    if (letzterErgebniseingangErfasstVorstufe01 == null || letzterErgebniseingangErfasstVorstufe01.hasErfasser(erfasser)) {
      return Ergebniseingabestufe.VORSTUFE_01;
    }

    if (letzterErgebniseingangErfasstVorstufe02 == null || letzterErgebniseingangErfasstVorstufe02.hasErfasser(erfasser)) {
      return Ergebniseingabestufe.VORSTUFE_02;
    }

    return Ergebniseingabestufe.ENDSTUFE;
  }

  private boolean isErgebniseingangErfasserErfasst() {
    return getStimmeingabeSecurity().isAktuellerErfasserErfasst();
  }

  private boolean isErgebniseingangErfasserKontrolliert() {
    return getStimmeingabeSecurity().isAktuellerErfasserKontrolliert();
  }

  @Override
  protected NlStimmeingabeSecurity getStimmeingabeSecurity() {
    return (NlStimmeingabeSecurity) super.getStimmeingabeSecurity();
  }

  @Override
  protected String getStimmeingabenKopfzahlBezeichnung() {
    if (isVergleichErgebniseingangVorhanden()) {
      return getMessage("stimmeingabe.erfassen.tabelle.bezeichnung." + Ergebniseingabestufe.VORSTUFE_02.name());
    }

    return super.getStimmeingabenKopfzahlBezeichnung();
  }

  @Override
  protected String getStimmeingabenKopfzahlVergleichBezeichnung() {
    return getMessage("stimmeingabe.erfassen.tabelle.bezeichnung." + Ergebniseingabestufe.VORSTUFE_01.name());
  }

  @Override
  public String getStimmeingabenGruppeStimmenListenstimmeBezeichnung() {
    if (isVergleichErgebniseingangVorhanden()) {
      return getMessage("stimmeingabe.erfassen.tabelle.bezeichnung." + Ergebniseingabestufe.VORSTUFE_02.name());
    }

    return super.getStimmeingabenGruppeStimmenListenstimmeBezeichnung();
  }

  @Override
  protected String getStimmeingabenGruppeStimmenListenstimmeVergleichBezeichnung() {
    return getMessage("stimmeingabe.erfassen.tabelle.bezeichnung." + Ergebniseingabestufe.VORSTUFE_01.name());
  }

  @Override
  protected Ergebniseingang findVergleichErgebniseingang(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingangsart ergebniseingangsart, List<Ergebniseingabestufe> ergebniseingabestufen,
      Ergebniseingabestufe ergebniseingabestufe, Ergebniseingang letzterErgebniseingangAktuell) {
    if (isErgebniseingangErfasserKontrolliert() && letzterErgebniseingangAktuell != null && letzterErgebniseingangAktuell.hasErfasserSystem() && letzterErgebniseingangAktuell.isUngueltigMussFehler()
        && letzterErgebniseingangAktuell.getErgebniseingabestufe().isInErgebnisUebernehmbar()) {
      return findLetzterErgebniseingangInErgebniseingabestufeGueltig(wahldurchfuehrungsgebiet, ergebniseingangsart, Ergebniseingabestufe.VORSTUFE_01);
    }

    return super.findVergleichErgebniseingang(wahldurchfuehrungsgebiet, ergebniseingangsart, ergebniseingabestufen, ergebniseingabestufe, letzterErgebniseingangAktuell);
  }

  @Override
  public boolean isBtnRefreshRendered() {
    if (isErgebniseingangErfasserKontrolliert()) {
      return !isBtnVorstufe01UebernehmenRendered() && !isBtnVorstufe02UebernehmenRendered() && !isBtnFehlerBestaetigenRendered() && !isBtnEingaengeLoeschenAusVorstufenRendered();
    }

    return super.isBtnRefreshRendered();
  }

  public boolean isBtnVorstufe01UebernehmenRendered() {
    return isBtnVorstufeUebernehmenRendered();
  }

  public boolean isBtnVorstufe01UebernehmenDisabled() {
    return isBtnVorstufeUebernehmenDisabled();
  }

  public String getBtnVorstufe01UebernehmenLabel() {
    return getBtnVorstufeUebernehmenLabel(Ergebniseingabestufe.VORSTUFE_01);
  }

  @Redirect
  @UseCaseSecure(UseCasePermission.STIMMEINGABE_AUS_VORSTUFE_UEBERNEHMEN)
  public String vorstufe01InErgebnisUebernehmen() {
    return vorstufeInErgebnisUebernehmen(Ergebniseingabestufe.VORSTUFE_01);
  }

  public boolean isBtnVorstufe02UebernehmenRendered() {
    return isBtnVorstufeUebernehmenRendered();
  }

  public boolean isBtnVorstufe02UebernehmenDisabled() {
    return isBtnVorstufeUebernehmenDisabled();
  }

  public String getBtnVorstufe02UebernehmenLabel() {
    return getBtnVorstufeUebernehmenLabel(Ergebniseingabestufe.VORSTUFE_02);
  }

  @Redirect
  @UseCaseSecure(UseCasePermission.STIMMEINGABE_AUS_VORSTUFE_UEBERNEHMEN)
  public String vorstufe02InErgebnisUebernehmen() {
    return vorstufeInErgebnisUebernehmen(Ergebniseingabestufe.VORSTUFE_02);
  }

  private boolean isBtnVorstufeUebernehmenRendered() {
    return hasPermission(UseCasePermission.STIMMEINGABE_AUS_VORSTUFE_UEBERNEHMEN) && isVergleichErgebniseingangVorhanden();
  }

  private boolean isBtnVorstufeUebernehmenDisabled() {
    return false;
  }

  private String getBtnVorstufeUebernehmenLabel(Ergebniseingabestufe ergebniseingabestufe) {
    return getMessage("stimmeingabe.erfassen.action.in.erebnis.uebernehmen." + ergebniseingabestufe.name());
  }

  private String vorstufeInErgebnisUebernehmen(Ergebniseingabestufe ergebniseingabestufe) {
    Ergebniseingang ergebniseingangVorstufe = findLetzterErgebniseingangInErgebniseingabestufeGueltig(getAktuellerErgebniseingang(), ergebniseingabestufe);

    getStimmeingabeBoundaryCreate().uebernimmErgebniseingangAusVorstufeInEndstufeOhneVergleichPlausibilisierung(getErgebniseingangErfasser(), ergebniseingangVorstufe, getStimmeingabePlausiBuilder());

    return resetEingabe();
  }

  @Override
  public boolean isBtnEingaengeLoeschenRendered() {
    return super.isBtnEingaengeLoeschenRendered() && !isBtnEingaengeLoeschenAusVorstufenRendered();
  }

  public boolean isBtnEingaengeLoeschenAusVorstufenRendered() {
    return hasPermission(UseCasePermission.STIMMEINGABE_DELETE_VORSTUFEN) && isLetzterErgebniseingangErfasstInVorstufeVorhanden() && isAktuellerErgebniseingangErgebniseingabestufeLaesstEingabeZu()
        && isLetzterErgebniseingangAktuellVorhandenHavingStatusUngueltig();
  }

  public boolean isBtnEingaengeLoeschenAusVorstufenDisabled() {
    return false;
  }

  public String getBtnEingaengeLoeschenAusVorstufenLabel() {
    boolean letzterErgebniseingangErfasstInVorstufe01Vorhanden = getLetzterErgebniseingangErfasstInErgebniseingabestufe(Ergebniseingabestufe.VORSTUFE_01) != null;
    boolean letzterErgebniseingangErfasstInVorstufe02Vorhanden = getLetzterErgebniseingangErfasstInErgebniseingabestufe(Ergebniseingabestufe.VORSTUFE_02) != null;

    if (letzterErgebniseingangErfasstInVorstufe01Vorhanden && !letzterErgebniseingangErfasstInVorstufe02Vorhanden) {
      return getMessage("stimmeingabe.erfassen.action.stimmeingaben.aus.vorstufe1.loeschen");
    }

    if (!letzterErgebniseingangErfasstInVorstufe01Vorhanden && letzterErgebniseingangErfasstInVorstufe02Vorhanden) {
      return getMessage("stimmeingabe.erfassen.action.stimmeingaben.aus.vorstufe2.loeschen");
    }

    return getMessage("stimmeingabe.erfassen.action.stimmeingaben.aus.vorstufen.loeschen");
  }

  @Redirect
  @UseCaseSecure(UseCasePermission.STIMMEINGABE_DELETE_VORSTUFEN)
  public String eingaengeLoeschenAusVorstufen() {
    loescheStimmeingaben(singletonList(getGebiet()), getSelectedErgebnis(), stimmeingabeErfassungLoeschen, getErgebniseingangErfasser());

    return resetEingabe();
  }

  private NlWasStimmeingabeBoundaryCreate getStimmeingabeBoundaryCreate() {
    return (NlWasStimmeingabeBoundaryCreate) stimmeingabeBoundaryCreate;
  }

  private boolean isLetzterErgebniseingangErfasstInVorstufeVorhanden() {
    for (Ergebniseingabestufe ergebniseingabestufe : Ergebniseingabestufe.values()) {
      if (!ergebniseingabestufe.isInErgebnisUebernehmbar() && getLetzterErgebniseingangErfasstInErgebniseingabestufe(ergebniseingabestufe) != null) {
        return true;
      }
    }

    return false; // Bspw. nach Import von Stimmeingaben als Ergebniseingang in Endstufe...
  }

  @Override
  public Map<String, AdvancedDynaFormModel> getModelsLetzteErgebniseingaenge() {
    return emptyMap(); // ELECT-10366
  }

  @Override
  public Map<String, Map<String, Object>> getErfassungshinweise() {
    //ELECTVAPP-310
    Map<String, Map<String, Object>> erfassungshinweise = super.getErfassungshinweise();
    erfassungshinweise.remove(getMessage("stimmeingabe.erfassen.erfassungshinweise.stimmvergabe.header"));
    erfassungshinweise.remove(getMessage("stimmeingabe.erfassen.erfassungshinweise.einstellungen.header"));

    return erfassungshinweise;
  }

  @Override
  public boolean isErfassungGesperrt() {
    return super.isErfassungGesperrt() || isBackupEinspielen() && !isStimmeingabeErfassenViewStatusFehlerBestaetigen();
  }

  private boolean isBackupEinspielen() {
    Wahl wahl = getWahl();
    if (wahl == null) {
      return false;
    }
    return wahl.getCustomData(NlWahlCustomData.class).isBackupRestoreEnabled();
  }

  @Override
  public boolean isBtnErfassungEntsperrenDisabled() {
    return super.isBtnErfassungEntsperrenDisabled() || isBackupEinspielen();
  }
}
