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

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import javax.ejb.Stateless;
import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.background.scheduler.AbstractSchedulerServiceTransaction;
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.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.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.business.wahl.entity.WahlParameter;
import de.ivu.elect.business.wahl.entity.WahlParameterLoader;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;
import de.ivu.elect.jee.faces.Messages;

@Stateless
public class PraesentationAsynchronBereitstellenTransaction extends AbstractSchedulerServiceTransaction<WahlPK> {

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

  @Inject
  protected transient KonfigurationLoader konfigurationLoader;

  @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 Messages messages;

  @Inject
  private transient PraesentationBereitstellenService praesentationBereitstellenService;

  private static final long MAX_FEHLER_IN_FOLGE = 3;

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

  @Override
  public Set<WahlPK> bestimmeNaechstePakete(Set<WahlPK> inBearbeitung, int anzahlNeuerPakate) {

    var praesentationUploadArt = konfigurationLoader.resolveEnumValue(ElectKonfigurationWerte.PRAESENTATION_EXPORT_UPLOAD_ART, PraesentationUploadArt.class);
    if (praesentationUploadArt == null || !praesentationUploadArt.isASynchron()) {
      return Collections.emptySet();
    }

    Set<WahlPK> result = new HashSet<>();
    for (var wahl : getAktivierteWahlenZufaellig()) {
      if (!inBearbeitung.contains(wahl.getPk())) {
        var parameter = getParameter(wahl);
        if (parameter != null && StringUtils.isNotBlank(parameter.getWert())) {
          result.add(wahl.getPk());
          if (result.size() >= anzahlNeuerPakate) {
            return result;
          }
        }
      }
    }
    return result;
  }

  private WahlParameter getParameter(Wahl wahl) {
    return wahlParameterLoader.getParameter(wahl, WahlParameterWerte.PREASENTATIION_ASYNCHRONE_BEREITSTELLUNG_NOTWENDIG);
  }

  @Override
  public void bearbeitePaket(WahlPK paket) {
    var wahl = wahlDAO.findByPrimaryKey(paket);
    var zuVerarbeitendenWahlParameter = getParameter(wahl);
    var dateiPKErgebnisPK = praesentationAsynchronBereitstellenHelper.readAndResetWahlparameter(zuVerarbeitendenWahlParameter);
    if (StringUtils.isBlank(dateiPKErgebnisPK)) {
      return;
    }

    LOG.info("Stelle Präsentation für Wahl bereit: " + wahl + "; " + dateiPKErgebnisPK);
    DateiExport dateiExport = null;
    Ergebnis ergebnis = null;
    try {
      var split = dateiPKErgebnisPK.split(PraesentationBereitstellenService.SEPARATOR);

      var dateiExportId = Long.parseLong(split[0]);
      var 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 {
      var praesentationBereitstellenStatus = praesentationBereitstellenService.praesentationBereitstellen(dateiExport.getPk(), ergebnis,
          new PraesentationStatistik(), true);
      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) {
    var 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);
    }
  }
}
