
package de.ivu.elect.business.wahl.boundary;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;

import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.wahl.control.WahlResetTransaction;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.business.wahl.entity.WahlResetStatus;
import de.ivu.elect.business.wahl.entity.WahlResetStatusContainer;
import de.ivu.elect.business.wahl.entity.WahlResetStatusEntry;
import de.ivu.elect.jee.faces.Messages;


import static java.util.Arrays.asList;

/**
 *  on 07.06.2017.
 */
@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class WahlResetAsync {

  private static final Logger LOG = LoggerFactory.getLogger(WahlResetAsync.class);

  private static final Map<WahlPK, WahlResetStatusContainer> STATUS = new ConcurrentHashMap<>();

  @Inject
  private transient WahlResetTransaction wahlResetTransaction;

  @Inject
  private transient Messages messages;

  @Asynchronous
  public void reset(WahlPK wahlPK) {
    WahlResetStatusContainer statusContainer = getStatus(wahlPK);
    if (statusContainer.getStatus().isLoeschenMoeglich()) {
      try {
        statusContainer = new WahlResetStatusContainer(WahlResetStatus.LAEUFT);
        STATUS.put(wahlPK, statusContainer);

        for (BiFunction<WahlPK, WahlResetStatusContainer, WahlResetStatusContainer> action : getResetAktionen(wahlResetTransaction)) {
          statusContainer = action.apply(wahlPK, statusContainer);
          STATUS.put(wahlPK, statusContainer);
        }

        statusContainer = new WahlResetStatusContainer(WahlResetStatus.ABGESCHLOSSEN, statusContainer.getEntries());
        STATUS.put(wahlPK, statusContainer);
      } catch (Exception e) {
        LOG.warn("Fehler beim Zurücksetzen der Wahl: " + wahlPK + ", " + e.getMessage(), e);
        Throwable cause = e;
        while (cause != null) {
          statusContainer = new WahlResetStatusContainer(WahlResetStatus.FEHLER, statusContainer, new WahlResetStatusEntry("wahl.reset.error", cause.getMessage(), cause.getClass()));
          cause = cause.getCause();
        }

        STATUS.put(wahlPK, statusContainer);
      }

      dumpResult(wahlPK, statusContainer);
    } else {
      LOG.info("Wahl " + wahlPK + " wird bereits zurückgesetzt");
    }
  }

  // Für Replikation nicht asynchron starten
  public void resetSync(WahlPK wahlPK) {
    reset(wahlPK);
  }

  private void dumpResult(WahlPK wahlPK, WahlResetStatusContainer statusContainer) {
    LOG.info("Wahl zurückgesetzt: " + wahlPK);
    for (WahlResetStatusEntry wahlResetStatusEntry : statusContainer.getEntries()) {
      LOG.info(wahlResetStatusEntry.getTimestamp() + " - " + messages.getMessageForMessageKey(wahlResetStatusEntry.getMsgKey(),
          (Object[]) wahlResetStatusEntry.getParams()));
    }
    LOG.info("Status: " + statusContainer.getStatus());
  }

  public WahlResetStatusContainer getStatus(WahlPK wahlPK) {
    return STATUS.computeIfAbsent(wahlPK, x -> new WahlResetStatusContainer(WahlResetStatus.OHNE));
  }

  public static List<BiFunction<WahlPK, WahlResetStatusContainer, WahlResetStatusContainer>> getResetAktionen(WahlResetTransaction wahlResetTransaction) {
    List<BiFunction<WahlPK, WahlResetStatusContainer, WahlResetStatusContainer>> result = new ArrayList<>(getResetAktionenOhneReplikationOhneCacheReset(wahlResetTransaction));
    result.add(wahlResetTransaction::cacheReset);
    result.add(wahlResetTransaction::sendeReplikationsnachricht);
    return result;
  }

  public static List<BiFunction<WahlPK, WahlResetStatusContainer, WahlResetStatusContainer>> getResetAktionenOhneReplikationOhneCacheReset(WahlResetTransaction wahlResetTransaction) {
    return asList(wahlResetTransaction::starteReset,
        wahlResetTransaction::loescheErgebniseingaenge,
        wahlResetTransaction::loescheErgebnisseVorlaeufig,
        wahlResetTransaction::loescheErgebnisseEndgueltig,
        wahlResetTransaction::loescheImportStimmeingaben,
        wahlResetTransaction::loescheExportStimmeingaben, wahlResetTransaction::loescheExportErgebnis,
        wahlResetTransaction::loescheStimmeingabenAblage, wahlResetTransaction::loescheWahlbeteiligungen,
        wahlResetTransaction::loescheStimmzettel, wahlResetTransaction::loescheStimmzettelKopfdaten,
        wahlResetTransaction::setzeGebieteAufEingabeMoeglich, wahlResetTransaction::loescheGebietAusbbleibend, wahlResetTransaction::loescheGebietAWerte,
        wahlResetTransaction::setzeWahlAufVorlaeufigeErfassung,
        wahlResetTransaction::resetWahllokalsystemStatus,
        wahlResetTransaction::starteInternetExportFallsKonfiguriert
        );
  }

}
