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

import de.ivu.elect.business.datei.boundary.DateiDAO;
import de.ivu.elect.business.datei.boundary.DateiExportDAO;
import de.ivu.elect.business.datei.boundary.DateiImportDAO;
import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.ergebnis.boundary.ErgebnisDAO;
import de.ivu.elect.business.ergebnis.control.ErgebnisCreate;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.exchange.control.ImportExportStreamCreator;
import de.ivu.elect.business.exchange.entity.DateiExportArt;
import de.ivu.elect.business.exchange.entity.DateiImportArt;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.internet.entity.PraesentationGenerierenEvent;
import de.ivu.elect.business.konfiguration.entity.KonfigurationLoader;
import de.ivu.elect.business.konfiguration.entity.MandantKonfigurationWerte;
import de.ivu.elect.business.replikation.boundary.ReplikationErzeugen;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingangDAO;
import de.ivu.elect.business.stimmeingabe.control.GebietAusbleibendToggle;
import de.ivu.elect.business.stimmeingabe.control.LetzterEingangCache;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingabestatus;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.stimmzettel.boundary.StimmzettelDAO;
import de.ivu.elect.business.stimmzettel.boundary.StimmzettelKopfdatumDAO;
import de.ivu.elect.business.system.boundary.CacheResetSession;
import de.ivu.elect.business.wahl.boundary.WahldurchfuehrungDAO;
import de.ivu.elect.business.wahl.entity.*;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungEdit;
import de.ivu.elect.business.wahllokalsystem.boundary.WahllokalsystemStatusDAO;
import de.ivu.elect.business.wahllokalsystem.control.WahllokalsystemAktiv;
import de.ivu.elect.jee.faces.Messages;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ejb.Stateless;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import java.io.File;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.function.Predicate;

/**
 * on 07.06.2017.
 */
@Stateless
public class WahlResetTransaction {
  private static final Logger LOG = LoggerFactory.getLogger(WahlResetTransaction.class);
  @Inject
  private transient WahldurchfuehrungDAO wahldurchfuehrungDAO;

  @Inject
  private transient ErgebnisCreate ergebnisCreate;

  @Inject
  private transient ErgebnisDAO ergebnisDAO;

  @Inject
  protected transient Messages messages;

  @Inject
  private transient ErgebniseingangDAO ergebniseingangDAO;

  @Inject
  protected transient KonfigurationLoader konfigurationLoader;

  @Inject
  private transient DateiImportDAO dateiImportDAO;

  @Inject
  private transient DateiExportDAO dateiExportDAO;

  @Inject
  private transient DateiDAO dateiDAO;

  @Inject
  private transient ImportExportStreamCreator importExportStreamCreator;

  @Inject
  private transient ReplikationErzeugen replikationErzeugen;

  @Inject
  private transient GebietAusbleibendToggle gebietAusbleibendToggle;

  @Inject
  private transient WahlbeteiligungDAO wahlbeteiligungDAO;

  @Inject
  private transient WahlbeteiligungEdit wahlbeteiligungEdit;

  @Inject
  protected transient WahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO;

  @Inject
  private transient WahllokalsystemAktiv wahllokalsystemAktiv;

  @Inject
  private transient PraesentationGenerierenEvent praesentationGenerierenEvent;

  @Inject
  private transient WahllokalsystemStatusDAO wahllokalsystemStatusDAO;

  @Inject
  transient CacheResetSession cacheResetSession;

  @Inject
  private transient StimmzettelDAO stimmzettelDAO;

  @Inject
  private transient StimmzettelKopfdatumDAO stimmzettelKopfdatumDAO;

  @Inject
  private transient Instance<WahlResetCustomAction> customActions;

  @Inject
  private transient LetzterEingangCache letzterEingangCache;

  @Inject
  private transient WahlParameterLoader wahlParameterLoader;

  public void schreibeOptionen(Collection<WahlPK> wahlPKs, Map<WahlParameterWerte, Boolean> resetOptions) {
    LOG.info("Schreibe Wahlparameter: wahlPKs=" + wahlPKs + ", resetOptions=" + resetOptions);
    for (var wahlPK : wahlPKs) {
      var wahl = wahldurchfuehrungDAO.findByPrimaryKey(wahlPK);
      for (var entry : resetOptions.entrySet()) {
        var parameter = wahlParameterLoader.getParameter(wahl, entry.getKey());
        parameter.setWert(entry.getValue().toString());
      }
    }
  }

  public WahlResetStatusContainer starteReset(WahlPK wahlPK, WahlResetStatusContainer status) {
    LOG.info("Starte: " + wahlPK);
    return finish(status, "wahl.reset.start");
  }

  public WahlResetStatusContainer loescheErgebniseingaenge(WahlPK wahlPK, WahlResetStatusContainer status) {

    LOG.info("Lösche Ergebniseingänge: " + wahlPK);
    Wahl wahl = attach(wahlPK);

    letzterEingangCache.reset(wahlPK);
//    for (Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet : wahldurchfuehrungsgebietDAO.findByWahl(wahl)) {
//      wahldurchfuehrungsgebiet.setLetzterEingangVorlaeufig(null);
//      wahldurchfuehrungsgebiet.setLetzterEingangEndgueltig(null);
//    }

    var anzahlGeloescht = ergebniseingangDAO.deleteByWahl(wahl);

    return finish(status, "wahl.reset.ergebniseingaenge.geloescht", anzahlGeloescht);
  }

  public WahlResetStatusContainer loescheErgebnisseVorlaeufig(WahlPK wahlPK, WahlResetStatusContainer status) {
    return loescheErgebnisse(wahlPK, status, Ergebnisart.VORLAEUFIG);
  }

  public WahlResetStatusContainer loescheErgebnisseEndgueltig(WahlPK wahlPK, WahlResetStatusContainer status) {
    return loescheErgebnisse(wahlPK, status, Ergebnisart.ENDGUELTIG);
  }

  private WahlResetStatusContainer loescheErgebnisse(WahlPK wahlPK, WahlResetStatusContainer status, Ergebnisart ergebnisart) {
    LOG.info("Lösche Ergebnis: " + wahlPK + ", " + ergebnisart);
    Wahl wahl = attach(wahlPK);
    var ergebnisse = ergebnisDAO.findByWahlAndErgebnisart(wahl, ergebnisart);

    for (var ergebnis : ergebnisse) {
      LOG.info("Lösche Ergebnis: " + ergebnis);
      ergebnisDAO.remove(ergebnis);
    }

    LOG.info("Lege Ergebnis an: " + ergebnisart);
    ergebnisCreate.create(wahl, ergebnisart);

    return finish(status, "wahl.reset.ergebnis.geloescht", messages.lokalisiere(ergebnisart));
  }

  public WahlResetStatusContainer loescheImportStimmeingaben(WahlPK wahlPK, WahlResetStatusContainer status) {
    return finish(loescheImport(attach(wahlPK), DateiImportArt.STIMMEINGABE_IMPORT, status), "wahl.reset.stimmeingabeimporte.geloescht");
  }

  public WahlResetStatusContainer loescheStimmeingabenAblage(WahlPK wahlPK, WahlResetStatusContainer status) {
    var result = status;

    var wahldurchfuehrung = attach(wahlPK);

    var verzeichnisName = konfigurationLoader.resolveStringValue(wahldurchfuehrung.getMandant(), MandantKonfigurationWerte.STIMMEINGABE_ABLAGEVERZEICHNIS);
    if (StringUtils.isNotBlank(verzeichnisName)) {
      var verzeichnis = new File(verzeichnisName + File.separator + wahldurchfuehrung.getDatumWahlartId());
      result = loescheVerzeichnis(result, verzeichnis, alles -> true);
      result = new WahlResetStatusContainer(result, new WahlResetStatusEntry("wahl.reset.stimmeingabeablage.geloescht"));
    }

    return result;
  }

  public WahlResetStatusContainer loescheStimmzettel(WahlPK wahlPK, WahlResetStatusContainer status) {
    LOG.info("Lösche erfasste Stimmzettel: " + wahlPK);

    Wahl wahl = attach(wahlPK);

    var stimmzettelList = stimmzettelDAO.findByWahl(wahl);

    for (var stimmzettel : stimmzettelList) {
      stimmzettelDAO.remove(stimmzettel);
    }

    return finish(status, "wahl.reset.wahl.stimmzettel.geloescht");
  }

  public WahlResetStatusContainer loescheStimmzettelKopfdaten(WahlPK wahlPK, WahlResetStatusContainer status) {
    LOG.info("Lösche erfasste Stimmzettelkopfdaten: " + wahlPK);

    Wahl wahl = attach(wahlPK);

    var stimmzettelKopfdaten = stimmzettelKopfdatumDAO.findByWahl(wahl);

    for (var stimmzettelKopfdatum : stimmzettelKopfdaten) {
      stimmzettelKopfdatumDAO.remove(stimmzettelKopfdatum);
    }

    return finish(status, "wahl.reset.wahl.stimmzettel.kopfdaten.geloescht");
  }

  public WahlResetStatusContainer loescheExportStimmeingaben(WahlPK wahlPK, WahlResetStatusContainer status) {
    return finish(loescheExport(attach(wahlPK), DateiExportArt.STIMMZETTELGEBIET_EXPORT, status), "wahl.reset.stimmeingabeexporte.geloescht");
  }

  public WahlResetStatusContainer loescheExportErgebnis(WahlPK wahlPK, WahlResetStatusContainer status) {
    return finish(loescheExport(attach(wahlPK), DateiExportArt.ERGEBNIS_EXPORT, status), "wahl.reset.ergebnisexporte.geloescht");
  }

  public WahlResetStatusContainer loescheWahlbeteiligungen(WahlPK wahlPK, WahlResetStatusContainer status) {
    LOG.info("Lösche erfasste Wahlbeteiligung: " + wahlPK);

    Wahl wahl = attach(wahlPK);

    var wahlbeteiligungen = wahlbeteiligungDAO.findByWahl(wahl);

    for (var wahlbeteiligung : wahlbeteiligungen) {
      wahlbeteiligungEdit.delete(wahlbeteiligung);
    }

    return finish(status, "wahl.reset.wahl.wahlbeteiligungen.geloescht");
  }

  public WahlResetStatusContainer setzeGebieteAufEingabeMoeglich(WahlPK wahlPK, WahlResetStatusContainer status) {
    var wahl = attach(wahlPK);

    for (var wahldurchfuehrungsgebiet : wahldurchfuehrungsgebietDAO.findByWahl(wahl)) {
      wahldurchfuehrungsgebiet.setEingabestatusVorlaeufig(Ergebniseingabestatus.EINGABE_MOEGLICH);
      wahldurchfuehrungsgebiet.setEingabestatusEndgueltig(Ergebniseingabestatus.EINGABE_MOEGLICH);
      wahldurchfuehrungsgebiet.setErfasstIn(null);
    }
    return finish(status, "wahl.reset.wahl.gebiete.eingabe.moeglich");
  }


  public WahlResetStatusContainer loescheGebietAusbbleibend(WahlPK wahlPK, WahlResetStatusContainer status) {
    var wahl = attach(wahlPK);
    for (var wahldurchfuehrungsgebiet : wahldurchfuehrungsgebietDAO.findByWahl(wahl)) {
      gebietAusbleibendToggle.setzeGebietNichtMehrAusbleibend(wahldurchfuehrungsgebiet);
    }
    return finish(status, "wahl.reset.wahl.gebiete.text.praensentation.geloescht");
  }

  public WahlResetStatusContainer loescheGebietAWerte(WahlPK wahlPK, WahlResetStatusContainer status) {
    if (isEnabled(wahlPK, WahlParameterWerte.WAHL_RESET_OPTION_AWERTE_LOESCHEN)) {
      var wahl = attach(wahlPK);
      for (var wahldurchfuehrungsgebiet : wahldurchfuehrungsgebietDAO.findByWahl(wahl)) {
        wahldurchfuehrungsgebiet.resetAWerte();
      }
      return finish(status, "wahl.reset.wahl.gebiete.text.awerte.geloescht");
    }
    return status; // A-Werte nur bei Destatis löschen (WASDE-1773)
  }

  public WahlResetStatusContainer sendeReplikationsnachricht(WahlPK wahlPK, WahlResetStatusContainer status) {
    replikationErzeugen.replikationWahlZuruecksetzen(attach(wahlPK));
    return new WahlResetStatusContainer(status, new WahlResetStatusEntry("wahl.reset.wahl.replikation.gesendet"));
  }

  public WahlResetStatusContainer setzeWahlAufVorlaeufigeErfassung(WahlPK wahlPK, WahlResetStatusContainer status) {
    var wahl = attach(wahlPK);

    wahl.setErgebniseingangsart(Ergebniseingangsart.VORLAEUFIG);


    return finish(status, "wahl.reset.wahl.vorlaeufige.erfassung");
  }

  public WahlResetStatusContainer resetWahllokalsystemStatus(WahlPK wahlPK, WahlResetStatusContainer status) {
    if (wahllokalsystemAktiv.isAktiv()) {
      var wahl = attach(wahlPK);

      for (var wahllokalsystemStatus : wahllokalsystemStatusDAO.findByWahl(wahl)) {
        wahllokalsystemStatus.reset();
      }

      return finish(status, "wahl.reset.wahllokalsystem.status.reset");
    }
    return status;
  }

  public WahlResetStatusContainer restCustomActions(WahlPK wahlPK, WahlResetStatusContainer status) {

    var result = status;
    if (customActions.isUnsatisfied()) {
      return finish(result, "wahl.reset.wahl.custom.no.actions");
    }
    var wahl = attach(wahlPK);
    for (var wahlResetCustomAction : customActions) {
      for (var wahlResetStatusEntry : wahlResetCustomAction.reset(wahl)) {
        result = finish(result, wahlResetStatusEntry);
      }

    }
    return result;
  }

  public WahlResetStatusContainer starteInternetExportFallsKonfiguriert(WahlPK wahlPK, WahlResetStatusContainer status) {
    praesentationGenerierenEvent.sendChangeEvent();
    return finish(status, "wahl.reset.wahl.internet.export");
  }

  public WahlResetStatusContainer cacheReset(WahlPK wahlPK, WahlResetStatusContainer status) {
    try {
      cacheResetSession.resetCache();
      return finish(status, "wahl.reset.wahl.cache.reset");
    } catch (Exception e) {
      // ELECT-9492
      return finish(status, "wahl.reset.wahl.cache.reset.error", e.getMessage());
    }
  }

  protected Wahldurchfuehrung attach(WahlPK wahlPK) {
    return wahldurchfuehrungDAO.findByPrimaryKey(wahlPK);
  }

  private WahlResetStatusContainer loescheImport(Wahl wahl, DateiImportArt dateiImportArt, WahlResetStatusContainer status) {
    LOG.info("Lösche Importe: " + wahl + ", " + dateiImportArt);
    var result = status;

    for (var dateiImport : dateiImportDAO.findByDateiImportArtAndWahlOrderBeiErstellungszeitpunktDesc(dateiImportArt, wahl)) {
      var datei = dateiImport.getDatei();
      dateiImportDAO.remove(dateiImport);
      result = loescheDatei(result, datei);
    }
    LOG.info("Lösche Importe: " + wahl + ", " + dateiImportArt + " - done");
    return result;
  }

  private WahlResetStatusContainer loescheExport(Wahl wahl, DateiExportArt dateiExportArt, WahlResetStatusContainer status) {
    LOG.info("Lösche Exporte: " + wahl + ", " + dateiExportArt);
    var result = status;

    for (var dateiExport : dateiExportDAO.findByDateiExportArtAndWahlOrderBeiErstellungszeitpunktDesc(dateiExportArt, wahl, 10000000/*willlkürlich*/)) {
      var datei = dateiExport.getDatei();
      dateiExportDAO.remove(dateiExport);
      result = loescheDatei(result, datei);
    }
    LOG.info("Lösche Exporte: " + wahl + ", " + dateiExportArt + " - done");
    return result;
  }

  private WahlResetStatusContainer loescheDatei(WahlResetStatusContainer result, Datei datei) {
    result = loescheFile(result, datei.createFile(importExportStreamCreator));
    dateiDAO.remove(datei);
    return result;
  }

  protected WahlResetStatusContainer loescheFile(WahlResetStatusContainer result, File file) {
    try {
      LOG.info("Lösche Datei: " + file.getAbsolutePath());
      FileUtils.forceDelete(file);
    } catch (Exception e) {
      result = new WahlResetStatusContainer(result, new WahlResetStatusEntry("wahl.reset.file.delete.error", file.getAbsolutePath(), e.getMessage()));
    }
    return result;
  }

  protected WahlResetStatusContainer loescheVerzeichnis(WahlResetStatusContainer result, File verzeichnis, Predicate<String> zuLoeschen) {
    if (verzeichnis.isDirectory()) {
      var files = verzeichnis.listFiles();
      if (files != null) {
        for (var file : files) {
          if (file.isFile() && zuLoeschen.test(file.getName())) {
            result = loescheFile(result, file);
          } else if (file.isDirectory()) {
            result = loescheVerzeichnis(result, file, zuLoeschen);
          }
        }
      }
    }
    return result;
  }

  protected WahlResetStatusContainer finish(WahlResetStatusContainer status, String msgKey, Serializable... params) {
    return finish(status, new WahlResetStatusEntry(msgKey, params));
  }

  protected WahlResetStatusContainer finish(WahlResetStatusContainer status, WahlResetStatusEntry wahlResetStatusEntry) {
    return new WahlResetStatusContainer(status, wahlResetStatusEntry);
  }

  protected boolean isEnabled(WahlPK wahlPK, WahlParameterWerte wahlParameterWerte) {
    return wahlParameterLoader.resolveBooleanValue(attach(wahlPK), wahlParameterWerte);
  }

}
