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

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Nullable;
import javax.inject.Inject;

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

import de.ivu.elect.business.exchange.control.DateienImportierenBackgroundService;
import de.ivu.elect.business.exchange.entity.DateienImportierenVerzeichnisStatus;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeImportStatus;
import de.ivu.elect.business.wahl.boundary.WahlExtendedDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterExtendedLoader;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.presentation.search.WildcardConverter;


import static java.util.Collections.singletonList;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

public class StimmeingabeImportStatusSearch {

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

  @Inject
  private transient Messages messages;

  @Inject
  private transient WahlExtendedDAO wahlExtendedDAO;

  @Inject
  private transient WahlParameterExtendedLoader wahlParameterExtendedLoader;

  public StimmeingabeImportStatusSearch() {
    super();
  }

  public List<StimmeingabeImportStatus> getSearchResult(StimmeingabeImportStatusSearchParameters searchParameters) {
    List<StimmeingabeImportStatus> result = new ArrayList<>();
    Wahl wahl = wahlExtendedDAO.attach(searchParameters.getWahl());

    if (wahl == null) {
      return result;
    }

    Map<DateienImportierenVerzeichnisStatus, File> verzeichnisse = bestimmeVerzeichnisse(wahl);

    for (Map.Entry<DateienImportierenVerzeichnisStatus, File> entry : verzeichnisse.entrySet()) {
      DateienImportierenVerzeichnisStatus verzeichnisStatus = entry.getKey();
      File verzeichnis = entry.getValue();

      result.addAll(leseVerzeichnis(verzeichnis, verzeichnisStatus, searchParameters));
    }

    result.sort(Comparator.comparing(StimmeingabeImportStatus::getVerzeichnisArt).thenComparing(StimmeingabeImportStatus::getDateiname));

    return result;
  }

  private Collection<StimmeingabeImportStatus> leseVerzeichnis(File verzeichnis, DateienImportierenVerzeichnisStatus verzeichnisStatus, StimmeingabeImportStatusSearchParameters searchParameters) {
    Collection<StimmeingabeImportStatus> result = new ArrayList<>();

    if (searchParameters.getVerzeichnisArt() == null || searchParameters.getVerzeichnisArt().equals(verzeichnisStatus)) {
      Map<String, File> fileNachName = new HashMap<>();
      File[] files = verzeichnis.listFiles();

      if (files != null) {
        fileNachName = Arrays.stream(files)
            .filter(File::isFile)
            .collect(Collectors.toMap(File::getName, file -> file, (a, b) -> b));
      }

      for (Map.Entry<String, File> entry : fileNachName.entrySet()) {
        String fileName = entry.getKey();
        File file = entry.getValue();

        if (isRelevantFile(fileName, verzeichnisStatus, searchParameters)) {
          File logFile = bestimmeLogfile(fileName, verzeichnisStatus, fileNachName);
          String message = null;
          Date zeitstempelMessage = null;

          if (logFile != null) {
            message = leseFile(logFile);

            zeitstempelMessage = new Date(logFile.lastModified());
          }

          result.add(new StimmeingabeImportStatus(fileName, new Date(file.lastModified()), verzeichnisStatus, message, zeitstempelMessage));
        }
      }
    }

    return result;
  }

  private String leseFile(File logFile) {
    try {
      return FileUtils.readFileToString(logFile, StandardCharsets.UTF_8);
    } catch (IOException e) {
      LOG.warn("Fehler beim Lesen der Datei: " + logFile, e);
      return e.getMessage();
    }
  }

  @Nullable
  private File bestimmeLogfile(String fileName, DateienImportierenVerzeichnisStatus verzeichnisStatus, Map<String, File> fileNachName) {
    if (verzeichnisStatus.isErfolg()) {
      return fileNachName.get(fileName + getSuccesSuffix());
    }

    if (verzeichnisStatus.isFehler()) {
      return fileNachName.get(fileName + getErrorSuffix());
    }

    return null;
  }

  private boolean isRelevantFile(String fileName, DateienImportierenVerzeichnisStatus verzeichnisStatus, StimmeingabeImportStatusSearchParameters searchParameters) {
    if (isNotBlank(searchParameters.getDateiname()) && !isDateiInListe(fileName, singletonList(searchParameters.getDateiname()))) {
      return false;
    }

    if (!searchParameters.getDateinamen().isEmpty() && !isDateiInListe(fileName, searchParameters.getDateinamen())) {
      return false;
    }

    if (verzeichnisStatus.isErfolg()) {
      return !fileName.endsWith(getSuccesSuffix());
    }

    if (verzeichnisStatus.isFehler()) {
      return !isErrorFile(fileName);
    }

    return true;
  }

  protected boolean isErrorFile(String fileName) {
    return fileName.endsWith(getErrorSuffix());
  }

  private boolean isDateiInListe(String fileName, List<String> searchParameterValues) {
    return searchParameterValues.stream().anyMatch(value -> fileName.toLowerCase(Locale.GERMANY).startsWith(value.toLowerCase(Locale.GERMANY)) || fileName.matches(convertToFileNameRegEx(value)));
  }

  private String convertToFileNameRegEx(String string) {
    return new WildcardConverter().addAndConvertToStringMatcherExpression(string);
  }

  private String getErrorSuffix() {
    return getMessage(DateienImportierenBackgroundService.DATEI_IMPORT_BACKGROUND_ERROR_FILE_EXTENSION);
  }

  private String getSuccesSuffix() {
    return getMessage(DateienImportierenBackgroundService.DATEI_IMPORT_BACKGROUND_LOG_FILE_EXTENSION);
  }

  protected String getMessage(String key) {
    return messages.getMessageForMessageKey(key);
  }

  private Map<DateienImportierenVerzeichnisStatus, File> bestimmeVerzeichnisse(Wahl wahl) {
    Map<DateienImportierenVerzeichnisStatus, File> result = new HashMap<>();

    for (DateienImportierenVerzeichnisStatus dateienImportierenVerzeichnisStatus : DateienImportierenVerzeichnisStatus.values()) {
      WahlParameterWerte wahlParameterWerte = bestimmeParameter(dateienImportierenVerzeichnisStatus);
      String verzeichnisAsString = wahlParameterExtendedLoader.resolveStringValue(wahl, wahlParameterWerte);

      if (isBlank(verzeichnisAsString)) {
        return Collections.emptyMap();
      }

      File verzeichnis = new File(verzeichnisAsString);

      if (verzeichnis.isDirectory()) {
        result.put(dateienImportierenVerzeichnisStatus, verzeichnis);
      } else {
        return Collections.emptyMap();
      }
    }

    return result;
  }

  protected WahlParameterWerte bestimmeParameter(DateienImportierenVerzeichnisStatus dateienImportierenVerzeichnisStatus) {
    switch (dateienImportierenVerzeichnisStatus) {
      case IMPORTIERT :
        return WahlParameterWerte.STIMMEINGABEN_CUSTOM_FORMAT_IMPORT_ERFOLGREICH_VERZEICHNIS;
      case FEHLER :
        return WahlParameterWerte.STIMMEINGABEN_CUSTOM_FORMAT_IMPORT_FEHLER_VERZEICHNIS;
      case QUELLE :
        return WahlParameterWerte.STIMMEINGABEN_CUSTOM_FORMAT_IMPORT_VERZEICHNIS;
    }

    throw new IllegalStateException("Unbekannte dateienImportierenVerzeichnisArt=" + dateienImportierenVerzeichnisStatus);
  }
}
