package de.ivu.elect.business.internet.control;

import de.ivu.elect.business.datei.boundary.DateiExportDAO;
import de.ivu.elect.business.datei.entity.DateiExport;
import de.ivu.elect.business.datei.entity.DateiExportPK;
import de.ivu.elect.business.ergebnis.boundary.ErgebnisDAO;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.ErgebnisPK;
import de.ivu.elect.business.konfiguration.entity.ElectKonfigurationWerte;
import de.ivu.elect.business.konfiguration.entity.KonfigurationLoader;
import de.ivu.elect.business.mandant.boundary.MandantDAO;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.praesentation.entity.PraesentationStatistik;
import de.ivu.elect.business.praesentation.entity.PraesentationUploadArt;
import de.ivu.elect.business.system.boundary.SystemLogWriter;
import de.ivu.elect.business.system.entity.SystemLogQuelle;
import de.ivu.elect.business.wahl.boundary.WahldurchfuehrungDAO;
import de.ivu.elect.business.wahl.entity.*;
import de.ivu.elect.jee.faces.Messages;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import javax.ejb.Stateless;
import javax.inject.Inject;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Stateless
public class PraesentationAsynchronBereitstellen implements Serializable {

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

  @Inject
  protected transient KonfigurationLoader konfigurationLoader;

  @Inject
  private transient MandantDAO mandantDAO;

  @Inject
  private transient WahldurchfuehrungDAO wahldurchfuehrungDAO;

  @Inject
  private transient WahlParameterLoader wahlParameterLoader;

  @Inject
  private transient PraesentationAsynchronBereitstellenHelper praesentationAsynchronBereitstellenHelper;

  @Inject
  private transient SystemLogWriter systemLogWriter;

  @Inject
  private transient ErgebnisDAO ergebnisDAO;

  @Inject
  private transient DateiExportDAO dateiExportDAO;

  @Inject
  private transient PraesentationBereitstellenService praesentationBereitstellenService;

  @Inject
  private transient Messages messages;

  private static final Map<WahlPK, AtomicLong> ANZAHL_FEHLER_IN_FOLGE_PROWAHL = new ConcurrentHashMap<>();

  private static final long MAX_FEHLER_In_FOLGE = 3;

  public boolean isPraesentationAsynchronBereitZuStellen() {
    return getZuVerarbeitendenWahlParameter() != null;
  }

  @Nullable
  private WahlParameter getZuVerarbeitendenWahlParameter() {
    PraesentationUploadArt praesentationUploadArt = konfigurationLoader
      .resolveEnumValue(ElectKonfigurationWerte.PRAESENTATION_EXPORT_UPLOAD_ART, PraesentationUploadArt.class);
    if (praesentationUploadArt != null && praesentationUploadArt.isASynchron()) {
      List<Mandant> mandanten = new ArrayList<>(mandantDAO.findByAll());
      Collections.shuffle(mandanten); // Zufall => fair
      for (Mandant mandant : mandanten) {
        List<Wahldurchfuehrung> wahlen = wahldurchfuehrungDAO.findByAll(mandant);
        Collections.shuffle(wahlen); // Zufall => fair
        for (Wahldurchfuehrung wahldurchfuehrung : wahlen) {
          WahlParameter parameter = wahlParameterLoader.getParameter(wahldurchfuehrung, WahlParameterWerte.PREASENTATIION_ASYNCHRONE_BEREITSTELLUNG_NOTWENDIG);
          if (parameter != null && StringUtils.isNotBlank(parameter.getWert())) {
            return parameter;
          }
        }
      }
    }
    return null;
  }

  public void praesentationAsynchronBereitstellen() {
    WahlParameter zuVerarbeitendenWahlParameter = getZuVerarbeitendenWahlParameter();
    String dateiPKErgebnisPK = praesentationAsynchronBereitstellenHelper.readAnResetWahlparameter(zuVerarbeitendenWahlParameter);
    if (StringUtils.isBlank(dateiPKErgebnisPK)) {
      return;
    }
    Wahl wahl = zuVerarbeitendenWahlParameter.getWahl();
    LOG.info("Stelle Präsentation für Wahl bereit: " + wahl + "; " + dateiPKErgebnisPK);
    DateiExport dateiExport = null;
    Ergebnis ergebnis = null;
    try {
      String[] split = dateiPKErgebnisPK.split(PraesentationBereitstellenService.SEPARATOR);

      long dateiExportId = Long.parseLong(split[0]);
      long ergebnisId = Long.parseLong(split[1]);

      dateiExport = dateiExportDAO.findByPrimaryKey(new DateiExportPK(dateiExportId));
      ergebnis = ergebnisDAO.findByPrimaryKey(new ErgebnisPK(ergebnisId));

    } catch (Exception e) {
      systemLogWriter.writeWarnLogMessageFuerMandant(wahl, "Fehler beim Bereitstellen der Präsentation: " + wahl.getTk().toStringLokalisiert(messages) + ", " + dateiPKErgebnisPK, e,
              SystemLogQuelle.PRAESENTATION_BEREITSTELLEN);
      return;
    }

    if (dateiExport == null || ergebnis == null) {
      LOG.info("Ergebnis oder Datei nicht mehr vorhanden: " + wahl + "; " + dateiPKErgebnisPK + " - done");
      return;
    }

    try {
      PraesentationBereitstellenStatus praesentationBereitstellenStatus = praesentationBereitstellenService
              .praesentationBereitstellen(dateiExport.getPk(), ergebnis, new PraesentationStatistik());
      if (praesentationBereitstellenStatus.isFehlerfrei()) {
        LOG.info("Stelle Präsentation für Wahl bereit: " + wahl + "; " + dateiPKErgebnisPK + " - done");
        ANZAHL_FEHLER_IN_FOLGE_PROWAHL.computeIfAbsent(wahl.getPk(), x -> new AtomicLong(0L)).set(0l);
        return;
      }
    } catch (Exception e) {
      systemLogWriter
              .writeErrorLogMessageFuerMandant(wahl, "Fehler beim Kopieren der erstellten Praesentationsdatei für Wahl: " + wahl.getTk().toStringLokalisiert(messages), e,
                      SystemLogQuelle.PRAESENTATION_BEREITSTELLEN);
    }
    handleFehlerBeimHochladen(wahl);

  }

  private void handleFehlerBeimHochladen(Wahl wahl) {
    long anzahlFehlerGesamt = ANZAHL_FEHLER_IN_FOLGE_PROWAHL.computeIfAbsent(wahl.getPk(), x -> new AtomicLong(0L)).addAndGet(1L);
    LOG.info("Anzahl Fehler in Folge für Wahl: " + wahl + ", " + anzahlFehlerGesamt);
    if (anzahlFehlerGesamt > MAX_FEHLER_In_FOLGE) {
      praesentationAsynchronBereitstellenHelper.disableUploadService(wahl);
      systemLogWriter
              .writeErrorLogMessageFuerMandant(wahl, "Praesentation bereitstellen deaktiviert für Wahl: " + wahl.getTk().toStringLokalisiert(messages), SystemLogQuelle.PRAESENTATION_BEREITSTELLEN);
    }
  }
}
