package de.ivu.elect.business.system.control.files.cleaner;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;

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.datei.entity.ServerFileType;

/**
 * Teilt bestehende Dateien nach dem "neuen" Ablagemuster pro Wahl auf
 */
public class DateiDirektInAblageverzeichnisCleaner extends AbstractCleaner<File> implements FileCleaner<File> {

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

  @Inject
  private transient DateiDAO dateiDAO;

  @Inject
  private transient DateiImportDAO dateiImportDAO;

  @Inject
  private transient DateiExportDAO dateiExportDAO;

  @Override
  public void bereinige(File verzeichnis, FileCleanerBucket<File> bucket) throws Exception {
    var file = bucket.getBucket();
    if (file != null) {
      var name = file.getName();
      LOG.info("Datei zum Bereinigen direkt in Ablageverzeichnis gefunden: " + name);
      var dateien = dateiDAO.findByDateinamePfadNull(name);
      if (dateien.isEmpty()) {
        verschiebeInRecycleBin(file, verzeichnis);
      } else {
        var datei = dateien.get(0);
        var dateiExport = findeDateiExport(datei);
        if (dateiExport != null) {
          verschiebeInExportVerzeichnis(file, verzeichnis, dateiExport);
        } else {
          var dateiImport = findeDateiImport(datei);
          if (dateiImport != null) {
            verschiebeInImportVerzeichnis(file, verzeichnis, dateiImport);
          } else {
            verschiebeInLegacyVerzeichnis(file, verzeichnis, datei);
          }
        }
      }
    }
  }

  @Override
  public List<FileCleanerBucket<File>> getBuckets(File verzeichnis) throws Exception {
    var ersteDateiDirektInServerVerzeichnis = getErsteDateiDirektInServerVerzeichnis(verzeichnis);
    if (ersteDateiDirektInServerVerzeichnis == null) {
      return Collections.emptyList();
    }
    return Collections.singletonList(new FileCleanerBucket<>(ersteDateiDirektInServerVerzeichnis));
  }

  private void verschiebeInLegacyVerzeichnis(File file, File verzeichnis, Datei datei) throws IOException {
    verschiebe(file, verzeichnis, bestimmePfad(datei, ServerFileType.LEGACY), datei);
  }

  private void verschiebeInImportVerzeichnis(File file, File verzeichnis, DateiImport dateiImport) throws IOException {
    verschiebe(file, verzeichnis, bestimmePfad(dateiImport.getDatei(), dateiImport.getDateiArt().getServerFileType()), dateiImport.getDatei());
  }

  private void verschiebeInExportVerzeichnis(File file, File verzeichnis, DateiExport dateiExport) throws IOException {
    verschiebe(file, verzeichnis, bestimmePfad(dateiExport.getDatei(), dateiExport.getDateiArt().getServerFileType()), dateiExport.getDatei());
  }

  @Nullable
  private DateiImport findeDateiImport(Datei datei) {
    try {
      return dateiImportDAO.findByDatei(datei);
    } catch (NoResultException | NonUniqueResultException e) {
      return null;
    }
  }

  @Nullable
  private DateiExport findeDateiExport(Datei datei) {
    try {
      return dateiExportDAO.findByDatei(datei);
    } catch (NoResultException | NonUniqueResultException e) {
      return null;
    }
  }

  @Nullable
  private File getErsteDateiDirektInServerVerzeichnis(File verzeichnis) {
    var files = verzeichnis.listFiles();
    if (files != null) {
      return Arrays.stream(files).filter(File::isFile).findFirst().orElse(null);
    }
    return null;
  }

  @Override
  protected Logger getLog() {
    return LOG;
  }
}
