package de.ivu.elect.presentation.wahl;

import de.ivu.elect.business.protokollierung.entity.LoeschProtokollEvent;
import de.ivu.elect.business.wahl.boundary.WahlExtendedDAO;
import de.ivu.elect.business.wahl.boundary.WahlResetAsync;
import de.ivu.elect.business.wahl.entity.*;
import de.ivu.elect.jee.faces.PreConditionCheck;
import de.ivu.elect.jee.faces.UserLocale;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaBooleanProperty;
import de.ivu.elect.jee.presentation.dynamicform.MessageProperty;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyStringProperty;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.AbstractElectCreateView;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;
import de.ivu.elect.presentation.replikation.ReplikationPresentationStatus;

import javax.annotation.Nullable;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static de.ivu.elect.business.security.UseCasePermission.WAHL_RESET;

/**
 * on 07.06.2017.
 */
@OnlineHilfeId(value = "697")
@RequestScoped
@Named(WahlResetView.COMPONENT_NAME)
@View(viewId = WahlResetView.VIEW_ID, useCase = WAHL_RESET)
public class WahlResetView extends AbstractElectCreateView<Wahl> {

  public static final String COMPONENT_NAME = "wahlResetView";

  public static final String VIEW_ID = "/wahl/wahl-reset.xhtml";
  public static final int TIMEOUT_IN_MS = 200;

  private static final long serialVersionUID = 1L;

  private static final List<WahlParameterWerte> OPTIONEN = Arrays.asList(WahlParameterWerte.WAHL_RESET_OPTION_AWERTE_LOESCHEN);

  @Inject
  private transient UserLocale userLocale;

  @EJB
  private transient WahlResetAsync wahlResetAsync;

  @Inject
  private transient ReplikationPresentationStatus replikationPresentationStatus;

  @Inject
  private transient LoeschProtokollEvent loeschProtokollEvent;

  @Inject
  private transient WahlExtendedDAO wahlExtendedDAO;

  @Inject
  private transient WahlParameterExtendedLoader wahlParameterExtendedLoader;

  @Nullable
  private Map<WahlParameterWerte, Boolean> resetOptions;

  private boolean bestaetigung;

  @Nullable
  @PreConditionCheck
  public String checkCondition() {
    if (!isWahlSelected()) {
      return getStartViewId();
    }

    return null;
  }

  @Override
  protected Wahl getEntity() {
    return getSelectedWahl();
  }

  @Override
  protected void addProperties(FormBuilder formBuilder, Wahl entity) {

    var aufgeteilteWahlen = getAufgeteilteWahlen();
    if (!aufgeteilteWahlen.isEmpty()) {
      formBuilder.addProperty(new ReadOnlyStringProperty(getMessage("wahl.reset.aufgeteilte.wahlen"),
          aufgeteilteWahlen.stream().sorted(Comparator.comparing(u -> u.getMandant().getNummer())).map(w -> getMessage("wahl.reset.aufgeteilte.wahlen.mandant",
              w.getMandant().getNummer(),
              w.getMandant().getName())).collect(Collectors.joining(", "))));
    }

    var statusContainer = getStatusWahlRest(getSelectedWahl().getPk());
    for (var wahlResetStatusEntry : statusContainer.getEntries()) {
      formBuilder.addProperty(new ReadOnlyStringProperty(userLocale.formatTime(wahlResetStatusEntry.getTimestamp()),
          getMessage(wahlResetStatusEntry.getMsgKey(), (Object[]) wahlResetStatusEntry.getParams())));
    }
    formBuilder.addProperty(new ReadOnlyStringProperty(getMessage("wahl.reset.status"), getMessage(statusContainer.getStatus())));

    for (var aufgeteilteWahl : aufgeteilteWahlen) {
      var augeteiltStatus = getStatusWahlRest(aufgeteilteWahl.getPk());
      formBuilder.addProperty(new ReadOnlyStringProperty(getMessage("wahl.reset.status.aufgeteilte.wahlen.mandant", aufgeteilteWahl.getMandant().getNummer(),
          aufgeteilteWahl.getMandant().getName()), getMessage(augeteiltStatus.getStatus())));

    }

    if (isBerichtetAnWahl()) {
      formBuilder.addProperty(new MessageProperty(getMessage("wahl.reset.nicht.moeglich.bereichtet.an", getSelectedWahl().getBerichtetAn().getMandant().getNummer(),
          getSelectedWahl().getBerichtetAn().getMandant().getName())));
    }

    if (isBtnResetCustomRendered()) {
      var resetOptionsLocal = getResetOptions();
      if (!resetOptionsLocal.isEmpty()) {
        formBuilder.addSeparator("wahl.reset.option.separator");
        for (var entry : resetOptionsLocal.entrySet()) {
          formBuilder.addProperty(new LambdaBooleanProperty(getMessage(entry.getKey()), false, b -> resetOptionsLocal.put(entry.getKey(), b), () -> Boolean.TRUE.equals(resetOptionsLocal.get(entry.getKey()))));
        }
      }
      formBuilder.addSeparator("wahl.reset.bestaetigung.separator");
      formBuilder.addProperty(new LambdaBooleanProperty(getMessage("wahl.reset.bestaetigung"), true, this::setBestaetigung, this::isBestaetigung));

    }
  }

  private List<Wahl> getAufgeteilteWahlen() {
    return wahlExtendedDAO.findByBerichtetAn(getSelectedWahl());
  }

  private transient WahlResetStatusContainer wahlResetStatusContainer;

  private WahlResetStatusContainer getStatusWahlRest(WahlPK pk) {
    if (wahlResetStatusContainer == null) {
      wahlResetStatusContainer = wahlResetAsync.getStatus(pk);
    }
    return wahlResetStatusContainer;
  }

  @Nullable
  @Override
  @Redirect
  public String reset() {
    if (bestaetigung) {
      bestaetigung = false;
      loeschProtokollEvent.sendeLoeschProtokollEvent(getEntity(), getEntity(), "loesch.protokoll.wahl.reset", getEntity().getName(),
          userLocale.formatDate(getEntity().getWahldatum()));

      List<WahlPK> pks = new ArrayList<>();
      pks.addAll(getAufgeteilteWahlen().stream().map(Wahl::getPk).collect(Collectors.toList()));
      pks.add(getEntity().getPk()); // ELECT-16775 aktuelle Wahl als letztes zurücksetzen
      wahlResetAsync.reset(pks, getResetOptions());

      try {
        TimeUnit.MILLISECONDS.sleep(TIMEOUT_IN_MS); // kurz warten, bis asynchrone Ausführung "hoffentlich" gestartet ist
      } catch (InterruptedException e) {
        // ignore
      }
      addInfoMessage("wahl.reset.gestartet");
      resetOptions = null;
    } else {
      addErrorMessage("wahl.reset.bestaetigung.notwendig");
    }

    return VIEW_ID;
  }

  public boolean isBtnResetCustomRendered() {
    return isWahlSelected() && hasPermission(WAHL_RESET) && !isResetLaeuft() && !isSlave() && !isBerichtetAnWahl();
  }

  private boolean isBerichtetAnWahl() {
    var selectedWahl = getSelectedWahl();
    return selectedWahl != null && selectedWahl.getBerichtetAn() != null;
  }

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

  @Override
  public boolean isBtnResetRendered() {
    return false;
  }

  @Nullable
  @Override
  public String cancel() {
    return null;
  }

  @Override
  public boolean isBtnCancelRendered() {
    return false;
  }

  @Override
  public boolean isBtnSaveRendered() {
    return false;
  }

  @Nullable
  @Override
  public String save() {
    return null;
  }

  public boolean isResetLaeuft() {
    return getStatusWahlRest(getEntity().getPk()).getStatus().isZurueckSetzenLaeuftGerade();
  }

  public Map<WahlParameterWerte, Boolean> getResetOptions() {
    if (resetOptions == null) {
      resetOptions = new LinkedHashMap<>();
      var selectedWahl = getSelectedWahl();
      if (selectedWahl != null) {
        for (var wahlParameterWerte : getPossibleResetOptions()) {
          resetOptions.put(wahlParameterWerte, wahlParameterExtendedLoader.resolveBooleanValue(selectedWahl, wahlParameterWerte));
        }
      }
    }
    return resetOptions;
  }

  protected List<WahlParameterWerte> getPossibleResetOptions() {
    return OPTIONEN;
  }

  public boolean isBestaetigung() {
    return bestaetigung;
  }

  public void setBestaetigung(boolean bestaetigung) {
    this.bestaetigung = bestaetigung;
  }
}




