
package de.ivu.elect.presentation.stimmeingabe.menu;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietSessionSelect;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingabestatuswechelPruefer;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingangKonfiguration;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingangStatus;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingabestatus;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.wahl.boundary.WahlSessionSelect;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.presentation.menu.AbstractSchrittMenuItem;
import de.ivu.elect.presentation.replikation.ReplikationPresentationStatus;
import de.ivu.elect.presentation.security.AbstractGebietSecurity;
import de.ivu.elect.presentation.stimmeingabe.StimmeingabeErfassenViewErgebniseingangsartStore;

public abstract class AbstractErfassungsschrittMenuItem extends AbstractSchrittMenuItem {

  private static final long serialVersionUID = 1L;

  public static final String POLL_WIDGET_STOP = "PF('frmStatusPollWidget').stop();";

  protected static final List<Ergebniseingabestatus> EINGABE_MOEGLICH_STATUS = Arrays.asList(Ergebniseingabestatus.EINGABE_MOEGLICH,
      Ergebniseingabestatus.EINGABE_MOEGLICH_EXTERN,
      Ergebniseingabestatus.EINGABE_MOEGLICH_INTERN);

  @Inject
  private transient StimmeingabeErfassenViewErgebniseingangsartStore stimmeingabeErfassenViewErgebniseingangsartStore;

  @Inject
  private transient WahlSessionSelect wahlSessionSelect;

  @Inject
  private transient WahldurchfuehrungsgebietSessionSelect wahldurchfuehrungsgebietSessionSelect;

  @Inject
  protected transient ReplikationPresentationStatus replikationPresentationStatus;

  @Inject
  private transient ErgebniseingabestatuswechelPruefer ergebniseingabestatuswechelPruefer;

  @Inject
  private transient ErgebniseingangStatus ergebniseingangStatus;

  @Inject
  private transient ErgebniseingangKonfiguration ergebniseingangKonfiguration;

  public AbstractErfassungsschrittMenuItem() {
    super();
  }

  @Override
  public boolean isRendered() {
    return getWahl() != null && getGebiet() != null && ergebniseingangKonfiguration.isErgebniseingangsartUnterstuetzt(getWahl(),
        getExpectedErgebniseingangsart());
  }

  @Override
  public boolean isTopicActiveInternal() {
    return getExpectedErgebniseingangsart().equals(getErgebniseingangsart()) && getExpectedErgebniseingabestatus().contains(getErgebniseingabestatusGebiet());
  }

  protected abstract Collection<Ergebniseingabestatus> getExpectedErgebniseingabestatus();

  protected abstract Ergebniseingangsart getExpectedErgebniseingangsart();

  protected Ergebniseingabestatus getErgebniseingabestatusGebiet() {
    return getGebiet().getErgebniseingabestatus(getErgebniseingangsart().korrespondierendeErgebnisart());
  }

  protected Ergebniseingangsart getErgebniseingangsart() {
    return stimmeingabeErfassenViewErgebniseingangsartStore.getErgebniseingangsart(getWahl());
  }

  protected Wahldurchfuehrungsgebiet getGebiet() {
    return wahldurchfuehrungsgebietSessionSelect.getEntity();
  }

  @Override
  protected boolean isDisabledInternal() {
    return !isRechtVorhanden() || !isBearbeitenRechtVorhanden() || isTopicActive() || isSlave() || !isUebergangMoeglich();
  }

  private boolean isRechtVorhanden() {
    // ELECTDD-595
    return getExpectedErgebniseingabestatus().stream()
        .anyMatch(expectedErgebniseingabestatus -> hasUseCasePermission(expectedErgebniseingabestatus.getNotwendigesRecht()));
  }

  private transient String dialogName = null;

  @Nullable
  @Override
  public String getDialogName() {
    if (dialogName == null) {

      if (getExpectedErgebniseingangsart().equals(getErgebniseingangsart())) {
        dialogName = getDialogNameInternal();
      } else {
        dialogName = "ErgebnisartUmschalten";
      }
    }
    if (StringUtils.isBlank(dialogName)) {
      return null;
    }
    return dialogName;

  }

  protected abstract String getDialogNameInternal();

  protected boolean isUebergangMoeglich() {

    if (getExpectedErgebniseingangsart().equals(getErgebniseingangsart())) {
      return getExpectedErgebniseingabestatus().stream().anyMatch(this::isStatusUebergangMoeglich);
    }

    if (!isRechtFuerErgebniseingangsartWechelnVorhanden()) {
      return false;
    }

    if (getGebiet().hasUebergeordnetesGebiet()) {
      return false; // Wechsel der Erfassungsart nur auf oberstem Gebiet möglich
    }

    if (!getErgebniseingangsart().isVorlaeufig() || getErgebniseingabestatusGebiet().isFreigegeben() /* Vorlaeufig nach Endgültig nur in Status freigegeben */) {
      // Eingabestatus in jeweils anderer Eingangsart notwendig
      return getExpectedErgebniseingabestatus().contains(
          getGebiet().getErgebniseingabestatus(getErgebniseingangsart().getArtNachWechsel().korrespondierendeErgebnisart()));
    }

    return false;
  }

  private boolean isRechtFuerErgebniseingangsartWechelnVorhanden() {
    return hasUseCasePermission(UseCasePermission.WAHL_ERGEBNISEINGANGSART_WECHSELN);
  }

  private boolean isBearbeitenRechtVorhanden() {
    return getBearbeitenGebietSecurity().isRechtVorhanden(getSelectedGebiet());
  }

  @Override
  public String getStyleClass() {
    return super.getStyleClass() + getSelectedSchrittStyleClass();
  }

  protected String getSelectedSchrittStyleClass() {

    if (isSelectedSchritt(getGebiet())) {
      return " selected_schritt";
    }

    return "";
  }

  protected boolean isSelectedSchritt(Wahldurchfuehrungsgebiet gebiet) {
    if (getExpectedErgebniseingangsart().isVorlaeufig()) {
      return getExpectedErgebniseingabestatus().contains(gebiet.getEingabestatusVorlaeufig());
    }
    if (getExpectedErgebniseingangsart().isEndgueltig()) {
      return getExpectedErgebniseingabestatus().contains(gebiet.getEingabestatusEndgueltig());
    }
    return false;
  }

  private transient Boolean isSlave;

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

  protected boolean isStatusUebergangMoeglich(Ergebniseingabestatus neuerStatus) {
    return ergebniseingabestatuswechelPruefer
        .isStatusuebergangMoeglich(getSelectedErgebnis(), getGebiet(), neuerStatus,
            hasUseCasePermission(UseCasePermission.GEBIET_ERGEBNISEINGABESTATUS_SETZEN_FREIGEGEBEN_ZURUECKNEHMEN))
        .isUebergangMoeglich();
  }

  protected Ergebnis getSelectedErgebnis() {
    return stimmeingabeErfassenViewErgebniseingangsartStore.getSelectedErgebnis();
  }

  private Wahl getWahl() {
    return wahlSessionSelect.getEntity();
  }

  @Override
  protected AbstractGebietSecurity getGebietSecurity() {
    return getEinsehenGebietSecurity();
  }

  protected boolean isAusbleibendOderGeperrt(Ergebniseingabestatus ergebniseingabestatusGebiet) {
    return ergebniseingabestatusGebiet.isAusbleibend() || ergebniseingabestatusGebiet.isGesperrt();

  }

  protected boolean isErgebniseingangsartVerschiedenOderNichtGeperrtAusbleibend(Ergebniseingangsart ergebniseingangsart, Ergebniseingabestatus ergebniseingabestatusGebiet) {
    return !(ergebniseingangsart.equals(getExpectedErgebniseingangsart()) && isAusbleibendOderGeperrt(ergebniseingabestatusGebiet));
  }

  @Override
  protected String getOnClick() {
    if (StringUtils.isNotBlank(getDialogName())) {
      return POLL_WIDGET_STOP;
    }
    return null;
  }

  protected String setzeGebietStatus(Ergebniseingabestatus status) {
    Wahldurchfuehrungsgebiet gebiet = getGebiet();
    Ergebniseingabestatus result = ergebniseingangStatus.setzeGebietErgebniseingabestatus(gebiet, status, getSelectedErgebnis());
    if (status.equals(result)) {
      return statusWechselErfolgreich(status, gebiet);
    }

    addWarningMessage("ergebniseingang.status.alter.gebiet.status", getMessage(result));

    return FacesContextUtil.getCurrentViewId();
  }

  @Nonnull
  protected String statusWechselErfolgreich(Ergebniseingabestatus status, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    addInfoMessage("ergebniseingang.status.neuer.gebiet.status", getMessage(status));
    return FacesContextUtil.getCurrentViewId();
  }

  protected void addWarningMessage(String msgKey, Object... params) {
    this.getMessages().addWarnMessage(msgKey, params);
  }

  @Override
  public String getIcon() {
    return getExpectedErgebniseingabestatus().stream().map(Ergebniseingabestatus::getIcon).filter(StringUtils::isNotBlank).findFirst().orElse("");
  }

  @Override
  public String getTitle() {
    if (isTopicActive()) {
      return getMessage(getTitleMsgKeyActive());
    }
    return getMessage(getTitleMsgKeyNotActive());
  }

  protected String getTitleMsgKeyNotActive() {
    return getLabelKey() + ".not.active";
  }

  protected String getTitleMsgKeyActive() {
    return getLabelKey() + ".active";
  }

  protected boolean isBeforeErgebniseingabestatus() {
    Ergebniseingabestatus ergebniseingabestatusGebiet = getErgebniseingabestatusGebiet();
    return getExpectedErgebniseingabestatus().stream()
        .anyMatch(expectedErgebniseingabestatus -> expectedErgebniseingabestatus.ordinal() < ergebniseingabestatusGebiet.ordinal());
  }
}

