
package de.ivu.elect.presentation.wahl;

import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.NoResultException;

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.WusFeaturesAktiviert;
import de.ivu.elect.business.gebietsbaum.boundary.AWerteReset;
import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.control.GebietByBreadthFirstComparator;
import de.ivu.elect.business.gebietsbaum.entity.AbstractGebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gebietsbaum.entity.WahldurchfuehrungsgebietProperties;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.security.control.ElectCommonPrincipal;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingangKonfiguration;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.wahl.boundary.WahlErgebniseingangsartWechseln;
import de.ivu.elect.business.wahl.boundary.WahldurchfuehrungSessionSelect;
import de.ivu.elect.business.wahl.control.WahlGebietsartSystemStatusHelper;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlErgebniseingangUebernahmeArt;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;
import de.ivu.elect.business.wahl.entity.WahldurchfuehrungProperties;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.faces.PreConditionCheck;
import de.ivu.elect.jee.presentation.dynamicform.AdvancedDynaFormModel;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyBooleanProperty;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyStringProperty;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.jee.presentation.security.StartViewResolver;
import de.ivu.elect.jee.presentation.view.AbstractView;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.ergebnis.ErgebnisStatusInfo;
import de.ivu.elect.presentation.ergebnis.ErgebnisartUmschaltenView;
import de.ivu.elect.presentation.ergebnis.ViewHavingErgebnisStatusInfo;
import de.ivu.elect.presentation.ergebnis.WasErgebnisStatusInfo;
import de.ivu.elect.presentation.gebietsbaum.GebietNummerFormatter;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;
import de.ivu.elect.presentation.replikation.ReplikationPresentationStatus;

@OnlineHilfeId("339")
@RequestScoped
@Named(WahlStatusuebersichtView.COMPONENT_NAME)
@View(viewId = WahlStatusuebersichtView.VIEW_ID, useCase = UseCasePermission.WAHL_STATUSUEBERSICHT_ANZEIGEN)
public class WahlStatusuebersichtView extends AbstractView implements ErgebnisartUmschaltenView, ViewHavingErgebnisStatusInfo {

  private static final long serialVersionUID = 1L;

  public static final String VIEW_ID = "/wahl/wahl-statusuebersicht.xhtml";

  public static final String COMPONENT_NAME = "wahlStatusuebersichtView";

  private static final String KEIN_TEMPLATE = "";

  @Inject
  private transient ElectCommonPrincipal principal;

  @Inject
  private transient WahldurchfuehrungSessionSelect wahldurchfuehrungSelect;

  @Inject
  private transient WahldurchfuehrungsgebietExtendedDAO wahldurchfuehrungsgebietDao;

  @Inject
  private transient ErgebniseingangKonfiguration ergebniseingangKonfiguration;

  @Inject
  private transient WahlErgebniseingangsartWechseln wahlErgebniseingangsartWechseln;

  @Inject
  private transient ReplikationPresentationStatus replikationPresentationStatus;

  @Inject
  private transient StartViewResolver startViewResolver;

  @Inject
  private transient WusFeaturesAktiviert wusFeaturesAktiviert;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Inject
  private transient GebietNummerFormatter gebietNummerFormatter;

  @Inject
  private transient WahlGebietsartSystemStatusHelper wahlGebietsartSystemStatusHelper;

  @Inject
  private transient AWerteReset aWerteReset;

  private transient AdvancedDynaFormModel model;

  public WahlStatusuebersichtView() {
    super();
  }

  @PreConditionCheck
  public String checkWahlSelected() {
    if (isWahlSelected()) {
      return null;
    }

    return startViewResolver.getStartViewId();
  }

  public void preRenderView() {
    // Do nothing...
  }

  private boolean isWahlSelected() {
    return wahldurchfuehrungSelect.isSelected();
  }

  public Wahldurchfuehrung getWahl() {
    return wahldurchfuehrungSelect.getEntity();
  }

  public String getWahlName() {
    if (getWahl() == null) {
      return "";
    }

    return getWahl().getName();
  }

  public AdvancedDynaFormModel getModel() {
    if (model == null) {
      FormBuilder formBuilder = new FormBuilder();

      if (isWahlSelected()) {
        Wahldurchfuehrung wahldurchfuehrung = getWahl();

        if (!wahlGebietsartSystemStatusHelper.pruefe(wahldurchfuehrung.getPk())) {
          addErrorMessage("wahl.gebietart.systemebene.passt.nicht", wahlGebietsartSystemStatusHelper.getErrorParams(wahldurchfuehrung.getPk()));
        }

        addPropertiesPre(formBuilder, wahldurchfuehrung);

        Wahl berichtetAn = wahldurchfuehrung.getBerichtetAn();

        if (berichtetAn != null) {
          String berichtetAnWahl = getMessage("wahl.status.berichtet.an.message", berichtetAn.getName(), berichtetAn.getMandant().getName(),
              berichtetAn.getMandant().getNummer());

          formBuilder.addProperty(new ReadOnlyStringProperty(getMessage("wahl.status.berichtet.an"), berichtetAnWahl));

          List<Gebiet> stimmbezirkeNichtImZielMandant = gebietExtendedDAO.findByWahl(wahldurchfuehrung).stream().filter(AbstractGebiet::isErfassungsgebiet)
              .filter(gebiet -> AbstractGebiet.findeGebietBeiAufteilungHandleWithCare(gebiet, berichtetAn, gebietExtendedDAO) == null).collect(toList());

          // ELECTKOM-472 Fehler anzeigen, wenn Fall eintritt
          if (!stimmbezirkeNichtImZielMandant.isEmpty()) {
            String nichtZugeordneteGebiete = stimmbezirkeNichtImZielMandant.stream().map(gebietNummerFormatter::getDarstellung).collect(joining(", "));

            addErrorMessage("wahl.aufgeteilt.gebiete.nicht.zugeordnet", berichtetAnWahl, nichtZugeordneteGebiete);

            formBuilder.addProperty(new ReadOnlyStringProperty(getMessage("wahl.aufgeteilt.gebiete.nicht.zugeordnet.label"), nichtZugeordneteGebiete));
          }
        }

        if (hasUseCasePermission(UseCasePermission.WAHL_FUER_SUBSYSTEME_KONFIGURIEREN)) {
          List<Gebiet> gebieteVerantwortlich = gebietExtendedDAO.findByWahlMitVerantwortlicherMandant(wahldurchfuehrung).stream().sorted(new GebietByBreadthFirstComparator()).collect(toList());

          for (Gebiet verantwortlich : gebieteVerantwortlich) {
            formBuilder.addProperty(new ReadOnlyStringProperty(getMessage("wahl.status.verantwortlich"), getMessage("wahl.status.verantworlich.message",
                verantwortlich.getVerantwortlicherMandant().getName(), verantwortlich.getVerantwortlicherMandant().getNummer(), gebietNummerFormatter.getDarstellung(verantwortlich))));
          }
        }

        WahldurchfuehrungProperties wahldurchfuehrungProperties = wahldurchfuehrung.getProperties();

        Collection<Ergebniseingangsart> ergebniseingangsarten = ergebniseingangKonfiguration.collectErgebniseingangsarten(wahldurchfuehrung);
        boolean ergebniseingangsartRendered = ergebniseingangKonfiguration.isWechselErgebniseingangsartenUnterstuetzt(wahldurchfuehrung)
            || wahldurchfuehrung.getErgebniseingangsart() != null && !ergebniseingangsarten.contains(wahldurchfuehrung.getErgebniseingangsart());

        if (ergebniseingangsartRendered) {
          formBuilder.addProperty(wahldurchfuehrungProperties.getErgebniseingangsartProperty()).readonly();
        }

        boolean awerteHinterlegt = isAwerteHinterlegt();
        formBuilder.addProperty(new ReadOnlyBooleanProperty(getMessage("wahl.statusuebersicht.anzahl.wahlberechtigter.importiert.label"), awerteHinterlegt));
        if (awerteHinterlegt && getWurzelgebiet() != null) {
          Long anzahlGebieteOhneAwerteWennOberstesGebietAwerteVorhanden = aWerteReset.getAnzahlGebieteOhneAwerteWennOberstesGebietAwerteVorhanden(
              getWurzelgebiet().getPk());
          if (anzahlGebieteOhneAwerteWennOberstesGebietAwerteVorhanden != null) {
            boolean vollstaendig = anzahlGebieteOhneAwerteWennOberstesGebietAwerteVorhanden == 0;
            formBuilder.addProperty(new ReadOnlyBooleanProperty(getMessage("wahl.statusuebersicht.anzahl.wahlberechtigter.vollstaendig.label"), vollstaendig));
            if (!vollstaendig) {
              formBuilder.withWarningMessage(getMessage("wahl.statusuebersicht.anzahl.wahlberechtigter.unvollstaendig.warning"));
            }
          }

        }

        Wahldurchfuehrungsgebiet wurzelgebiet = getWurzelgebiet();

        if (wurzelgebiet != null) {
          WahldurchfuehrungsgebietProperties wahldurchfuehrungsgebietProperties = wurzelgebiet.getProperties();

          if (ergebniseingangKonfiguration.isErgebniseingangsartVorlaeufigUnterstuetzt(wahldurchfuehrung)) {
            formBuilder.addProperty(wahldurchfuehrungsgebietProperties.getEingabestatusVorlaeufigProperty()).readonly();
          }

          if (ergebniseingangKonfiguration.isErgebniseingangsartEndgueltigUnterstuetzt(wahldurchfuehrung)) {
            formBuilder.addProperty(wahldurchfuehrungsgebietProperties.getEingabestatusEndgueltigProperty()).readonly();
          }
        }

        formBuilder.addProperty(wahldurchfuehrungProperties.getSitzberechnungsalgorithmusProperty()).readonly();
      }

      model = formBuilder.createModel();
    }

    return model;
  }

  protected void addPropertiesPre(FormBuilder formBuilder, Wahldurchfuehrung wahldurchfuehrung) {
    // nichts zu tun
  }

  @Override
  public ErgebnisStatusInfo getErgebnisStatusInfo() {
    Wahldurchfuehrung wahldurchfuehrung = getWahl();

    if (wahldurchfuehrung != null) {
      return new WasErgebnisStatusInfo(getMessages(), wahldurchfuehrung, ergebniseingangKonfiguration, wahldurchfuehrung.getErgebniseingangsart());
    }

    return ErgebnisStatusInfo.EMPTY;
  }

  private Wahldurchfuehrungsgebiet getWurzelgebiet() {
    try {
      return wahldurchfuehrungsgebietDao.findByWahlWurzelgebiet(getWahl());
    } catch (NoResultException e) {
      if (wusFeaturesAktiviert.isWusOderWusWasZusammen()) {
        return null;
      }

      throw new IllegalStateException("In WAS sind Wahlen ohne Gebiete nicht zugelassen", e);
    }
  }

  @Override
  public boolean isBtnWechselnAufEndgueltigeErfassungOhneUebernahmeVorlaeufigerErgebniseingaengeRendered() {
    return isWechselnAufEndgueltigeErfassungMoeglich() && hasUseCasePermission(UseCasePermission.WAHL_ERGEBNISEINGANGSART_WECHSELN) && !isSlave();
  }

  @Override
  public boolean isBtnWechselnAufEndgueltigeErfassungOhneUebernahmeVorlaeufigerErgebniseingaengeDisabled() {
    return !isWechselnAufEndgueltigeErfassungMoeglich();
  }

  @Override
  public String getBtnWechselnAufEndgueltigeErfassungOhneUebernahmeVorlaeufigerErgebniseingaengeLabel() {
    String msgKey = "wahl.statusuebersicht.endgueltige.erfassung.aktivieren";

    if (isBtnWechselnAufEndgueltigeErfassungMitUebernahmeVorlaeufigerErgebniseingaengeRendered()) {
      msgKey += ".ohne.uebernahme.vorlaeufiger.ergebniseingaenge";
    }

    return getMessage(msgKey);
  }

  @Override
  @Redirect
  public String wechselnAufEndgueltigeErfassungOhneUebernahmeVorlaeufigerErgebniseingaenge() {
    return wechselnAufEndgueltigeErfassung(wahldurchfuehrung -> wahlErgebniseingangsartWechseln.wechselnAufEndgueltigeErfassung(wahldurchfuehrung, WahlErgebniseingangUebernahmeArt.DEAKTIVIERT));
  }

  @Override
  public boolean isBtnWechselnAufEndgueltigeErfassungMitUebernahmeVorlaeufigerErgebniseingaengeRendered() {
    return isWechselnAufEndgueltigeErfassungMoeglich() && hasUseCasePermission(UseCasePermission.WAHL_ERGEBNISEINGANGSART_WECHSELN_MIT_UEBERNAHME_VORHANDENER_ERGEBNISEINGAENGE)
        && wahlErgebniseingangsartWechseln.isUebernahmeVorlaeufigerErgebniseingaengeUnterstuetzt(getWahl()).isAutomatischeErgebniseingangUebernahmeVonVorlaeufigNachEndgueltigMoeglich() && !isSlave();
  }

  @Override
  public boolean isBtnWechselnAufEndgueltigeErfassungMitUebernahmeVorlaeufigerErgebniseingaengeDisabled() {
    return !isWechselnAufEndgueltigeErfassungMoeglich();
  }

  // WASDE-1153
  protected boolean isSlave() {
    return replikationPresentationStatus.isSlave();
  }

  @Override
  public String getBtnWechselnAufEndgueltigeErfassungMitUebernahmeVorlaeufigerErgebniseingaengeLabel() {
    return getMessage("wahl.statusuebersicht.endgueltige.erfassung.aktivieren.mit.uebernahme.vorlaeufiger.ergebniseingaenge");
  }

  @Override
  public String getZielErgebnisartName() {
    Wahldurchfuehrung wahl = getWahl();
    if (wahl == null) {
      return "";
    }

    return getMessage(wahl.getErgebniseingangsart().getArtNachWechsel());
  }

  @Override
  public String getBtnErgebnisartUmschaltenLabel() {
    return getMessage("wahl.status.uebersicht.erfassung.umschalten", getZielErgebnisartName());
  }

  @Override
  public boolean isBtnErebnisartUmschaltenRendered() {
    return isBtnWechselnAufEndgueltigeErfassungOhneUebernahmeVorlaeufigerErgebniseingaengeRendered() && !isBtnWechselnAufEndgueltigeErfassungOhneUebernahmeVorlaeufigerErgebniseingaengeDisabled()
        || isBtnWechselnAufEndgueltigeErfassungMitUebernahmeVorlaeufigerErgebniseingaengeRendered() && !isBtnWechselnAufEndgueltigeErfassungMitUebernahmeVorlaeufigerErgebniseingaengeDisabled()
        || isBtnWechselnAufVorlaeufigeErfassungRendered() && !isBtnWechselnAufVorlaeufigeErfassungDisabled();
  }

  @Override
  @Redirect
  public String wechselnAufEndgueltigeErfassungMitUebernahmeVorlaeufigerErgebniseingaenge() {
    return wechselnAufEndgueltigeErfassung(wahldurchfuehrung -> wahlErgebniseingangsartWechseln.wechselnAufEndgueltigeErfassung(wahldurchfuehrung,
        wahlErgebniseingangsartWechseln.isUebernahmeVorlaeufigerErgebniseingaengeUnterstuetzt(wahldurchfuehrung)));
  }

  protected String wechselnAufEndgueltigeErfassung(Consumer<Wahldurchfuehrung> wahlErgebniseingangsartWechseln) {
    wahlErgebniseingangsartWechseln.accept(getWahl());

    addInfoMessage("wahl.erfassung.endgueltig.aktiviert");

    return FacesContextUtil.getCurrentViewId();
  }

  private boolean isWechselnAufEndgueltigeErfassungMoeglich() {
    return wahlErgebniseingangsartWechseln.isWechselnAufEndgueltigeErfassungMoeglich(getWahl());
  }

  @Override
  public boolean isBtnWechselnAufVorlaeufigeErfassungRendered() {
    return isWechselnAufVorlaeufigeErfassungMoeglich() && hasUseCasePermission(UseCasePermission.WAHL_ERGEBNISEINGANGSART_WECHSELN) && !isSlave();
  }

  @Override
  public boolean isBtnWechselnAufVorlaeufigeErfassungDisabled() {
    return !isWechselnAufVorlaeufigeErfassungMoeglich();
  }

  @Override
  public String getBtnWechselnAufVorlaeufigeErfassungLabel() {
    return getMessage("wahl.statusuebersicht.vorlaeufig.erfassung.aktivieren");
  }

  private boolean isWechselnAufVorlaeufigeErfassungMoeglich() {
    return wahlErgebniseingangsartWechseln.isWechselnAufVorlaeufigeErfassungMoeglich(getWahl());
  }

  @Override
  @Redirect
  public String wechselnAufVorlaeufigeErfassung() {
    wahlErgebniseingangsartWechseln.wechselnAufVorlaeufigeErfassung(getWahl());

    addInfoMessage("wahl.erfassung.vorlaeufig.aktiviert");

    return FacesContextUtil.getCurrentViewId();
  }

  public boolean isCustomButtonTemplatePresent() {
    return StringUtils.isNotBlank(getCustomButtonTemplate());
  }

  public String getCustomButtonTemplate() {
    return KEIN_TEMPLATE;
  }

  public boolean isCustomDialogTemplatePresent() {
    return StringUtils.isNotBlank(getCustomDialogTemplate());
  }

  public String getCustomDialogTemplate() {
    return KEIN_TEMPLATE;
  }

  protected boolean hasUseCasePermission(UseCasePermission permission) {
    return principal.hasUseCasePermission(permission);
  }

  private boolean isAwerteHinterlegt() {
    Gebiet wurzelgebiet = getWurzelgebiet();

    if (wurzelgebiet == null) {
      return false;
    }

    return wurzelgebiet.castTo(Wahldurchfuehrungsgebiet.class).getEntity().hasAnyAnzahlWahlberechtigte();
  }
}
