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

import java.io.File;
import java.io.Serializable;
import java.util.List;
import java.util.function.Predicate;

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

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

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.datei.entity.DateiExport;
import de.ivu.elect.business.datei.entity.DateiImport;
import de.ivu.elect.business.ergebnis.boundary.ErgebnisDAO;
import de.ivu.elect.business.ergebnis.control.ErgebnisCreate;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
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.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
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.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.stimmzettel.entity.Stimmzettel;
import de.ivu.elect.business.stimmzettel.entity.StimmzettelKopfdatum;
import de.ivu.elect.business.system.boundary.CacheResetSession;
import de.ivu.elect.business.wahl.boundary.WahldurchfuehrungDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.business.wahl.entity.WahlResetStatusContainer;
import de.ivu.elect.business.wahl.entity.WahlResetStatusEntry;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungEdit;
import de.ivu.elect.business.wahlbeteiligung.entity.Wahlbeteiligung;
import de.ivu.elect.business.wahllokalsystem.boundary.WahllokalsystemStatusDAO;
import de.ivu.elect.business.wahllokalsystem.control.WahllokalsystemAktiv;
import de.ivu.elect.business.wahllokalsystem.entity.WahllokalsystemStatus;
import de.ivu.elect.jee.faces.Messages;

/**
 *  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;


  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);

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

    Integer 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);
    List<Ergebnis> ergebnisse = ergebnisDAO.findByWahlAndErgebnisart(wahl, ergebnisart);

    for (Ergebnis ergebnis : ergebnisse) {
      LOG.info("Lösche Ergbenis: " + 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) {
    WahlResetStatusContainer result = status;

    Wahldurchfuehrung wahldurchfuehrung = attach(wahlPK);

    String verzeichnisName = konfigurationLoader.resolveStringValue(wahldurchfuehrung.getMandant(), MandantKonfigurationWerte.STIMMEINGABE_ABLAGEVERZEICHNIS);
    if (StringUtils.isNotBlank(verzeichnisName)) {
      File 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);

    List<Stimmzettel> stimmzettelList = stimmzettelDAO.findByWahl(wahl);

    for (Stimmzettel 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);

    List<StimmzettelKopfdatum> stimmzettelKopfdaten = stimmzettelKopfdatumDAO.findByWahl(wahl);

    for (StimmzettelKopfdatum 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);

    List<Wahlbeteiligung> wahlbeteiligungen = wahlbeteiligungDAO.findByWahl(wahl);

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

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

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

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


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

  public WahlResetStatusContainer loescheGebietAWerte(WahlPK wahlPK, WahlResetStatusContainer status) {
    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) {
    Wahldurchfuehrung 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()) {
      Wahldurchfuehrung wahl = attach(wahlPK);

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

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

  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);
    WahlResetStatusContainer result = status;

    for (DateiImport dateiImport : dateiImportDAO.findByDateiImportArtAndWahlOrderBeiErstellungszeitpunktDesc(dateiImportArt, wahl)) {
      Datei 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);
    WahlResetStatusContainer result = status;

    for (DateiExport dateiExport : dateiExportDAO.findByDateiExportArtAndWahlOrderBeiErstellungszeitpunktDesc(dateiExportArt, wahl, 10000000/*willlkürlich*/)) {
      Datei 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()) {
      File[] files = verzeichnis.listFiles();
      if (files != null) {
        for (File 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 new WahlResetStatusContainer(status, new WahlResetStatusEntry(msgKey, params));
  }

}
