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

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.function.Consumer;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.datei.boundary.DateiImportDAO;
import de.ivu.elect.business.datei.control.FileExchange;
import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.datei.entity.DateiImport;
import de.ivu.elect.business.datei.entity.DateiImportBerichtCustomData;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.exchange.entity.DateiImportArt;
import de.ivu.elect.business.exchange.entity.ImportStatus;
import de.ivu.elect.business.mandant.boundary.MandantDAO;
import de.ivu.elect.business.mandant.control.MandantCreate;
import de.ivu.elect.business.mandant.entity.AbstractMandant;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.mandant.entity.MandantArt;
import de.ivu.elect.business.mandant.entity.MandantPK;
import de.ivu.elect.business.mandant.entity.MandantStatus;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlOderWahltermin;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.jee.exchange.ImportOptions;

public abstract class AbstractDatenCreator {

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

  private static final String FILE_SEPARATOR = "/";

  private static final String FILE_SEPARATOR_WINDOWS = "\\";

  @Inject
  private transient ImportExportStreamCreator streamCreator;

  @Inject
  private transient FileExchange fileExchange;

  @Inject
  private transient MandantDAO mandantDAO;

  @Inject
  private transient DateiImportDAO dateiImportDao;

  @Inject
  private transient MandantCreate mandantCreate;

  @Inject
  private transient WahlDAO wahlDAO;

  public AbstractDatenCreator() {
    super();
  }

  public <ImportObject, Context, CustomDateiImportBerichtCustomData extends DateiImportBerichtCustomData> void importiere(
      AbstractImporter<ImportObject, Context, CustomDateiImportBerichtCustomData> importer, String filePath, FileFormat fileFormat,
      DateiImportArt dateiImportArt, Mandant mandant, WahlOderWahltermin wahlOderWahltermin, Context context, ImportOptions importOptions,
      Consumer<DateiImport> dateiImportHandler) {
    importiere(importer, filePath, fileFormat, dateiImportArt, mandant, wahlOderWahltermin, context, importOptions, dateiImportHandler,
        new DateiImportBerichtCustomData());
  }

  public <ImportObject, Context, CustomDateiImportBerichtCustomData extends DateiImportBerichtCustomData> void importiere(
      AbstractImporter<ImportObject, Context, CustomDateiImportBerichtCustomData> importer, String filePath, FileFormat fileFormat,
      DateiImportArt dateiImportArt, Mandant mandant, WahlOderWahltermin wahlOderWahltermin, Context context, ImportOptions importOptions,
      Consumer<DateiImport> dateiImportHandler, DateiImportBerichtCustomData cd) {
    DateiImport dateiImport = createDateiImport(filePath, fileFormat, mandant, wahlOderWahltermin, dateiImportArt, cd);

    dateiImportHandler.accept(dateiImport);

    importer.importieren(dateiImport, context, importOptions);
  }

  protected DateiImport createDateiImport(String filePath, FileFormat fileFormat, Mandant mandant, WahlOderWahltermin wahlOderWahltermin,
      DateiImportArt dateiArt, DateiImportBerichtCustomData dateiImportBerichtCustomData) {
    String dateiname = bestimmeDateiName(filePath);

    Datei datei = createDatei(dateiname, fileFormat, mandant, wahlOderWahltermin);
    datei.setDateiname(FILE_SEPARATOR + dateiname);

    DateiImport dateiImport = new DateiImport(dateiImportBerichtCustomData);
    dateiImport.setDateiArt(dateiArt);
    dateiImport.setDatei(datei);
    dateiImport.setImportStatus(ImportStatus.FORMAT_FESTGELEGT);

    dateiImportDao.persist(dateiImport);

    copyDateiToServerDir(dateiname, filePath);

    return dateiImport;
  }

  protected String bestimmeDateiName(String filePath) {
    if (filePath.contains(FILE_SEPARATOR)) {
      String[] dateinameAsArray = filePath.split(FILE_SEPARATOR);

      return dateinameAsArray[dateinameAsArray.length - 1];
    }

    if (filePath.contains(FILE_SEPARATOR_WINDOWS)) {
      String[] dateinameAsArray = filePath.split(FILE_SEPARATOR_WINDOWS + FILE_SEPARATOR_WINDOWS);

      return dateinameAsArray[dateinameAsArray.length - 1];
    }

    return filePath;
  }

  private Datei createDatei(String dateiname, FileFormat fileFormat, Mandant mandant, @Nullable WahlOderWahltermin wahlOderWahltermin) {
    FileType fileType = fileFormat.getFileType();

    Datei datei = fileType.createDatei();
    datei.setMandant(mandantDAO.attach(mandant));
    datei.setErstellungszeitpunkt(new Date());
    datei.setOriginalDateiname(dateiname);
    datei.setFileType(fileType);
    datei.setMimeType(fileType.getDefaultMimeType());
    datei.setFormat(fileFormat.getName());
    datei.setDateiname(dateiname);

    if (wahlOderWahltermin != null) {
      if (wahlOderWahltermin instanceof Wahltermin) {
        datei.setWahltermin((Wahltermin) wahlOderWahltermin);
      }

      if (wahlOderWahltermin instanceof Wahl) {
        datei.setWahl((Wahl) wahlOderWahltermin);
      }
    }

    return datei;
  }

  private void copyDateiToServerDir(String dateiname, String filePath) {
    try (InputStream is = getInputStream(filePath)) {
      getLog().info("Copy from InputStream [is=" + is + ", dateiname=" + filePath + "] to Server");
      try (OutputStream outputStreamForDatei = streamCreator.createOutputStreamForDatei(dateiname, null)) {
        fileExchange.copyInputStreamToOutputStream(is, outputStreamForDatei);
      }
    } catch (IOException ex) {
      throw new RuntimeException(ex.getMessage(), ex);
    }
  }

  protected InputStream getInputStream(String filePath) throws IOException {
    InputStream is = getClass().getResourceAsStream(FILE_SEPARATOR + filePath);

    if (is == null) {
      throw new IllegalStateException("Resource nicht gefunden! path=" + filePath);
    }

    return is;
  }

  public abstract Logger getLog();

  @Deprecated // Nur für TESTDATEN(!)
  public Mandant getSystemMandant(){
    return AbstractMandant.getSystemMandant(mandantDAO);
  }

  @Deprecated // Nur für TESTDATEN(!)
  public Mandant getCustomerMandant(){
    return AbstractMandant.getCustomerMandant(mandantDAO);
  }

  public MandantPK createMandant(Long nummer, String name) {
    return createMandant(nummer, name, null);
  }

  public MandantPK createMandant(Long nummer, String name, String beschreibung) {
    LOG.info("Erstelle Mandnat: " + nummer + ", " + name);
    Mandant mandant = new Mandant();
    mandant.setArt(MandantArt.CUSTOMER);
    mandant.setStatus(MandantStatus.AKTIVIERT);
    mandant.setNummer(nummer);
    mandant.setName(name);
    mandant.setBeschreibung(beschreibung);
    mandantCreate.save(mandant);
    LOG.info("Mandant erstellt: " + mandant);
    return mandant.getPk();
  }

  public Mandant landeMandant(MandantPK mandantPK) {
    return mandantDAO.findByPrimaryKey(mandantPK);
  }

  public MandantPK findeMandantZuNummer(Long mandantenNummer) {
    return mandantDAO.findByNummer(mandantenNummer).getPk();
  }

  protected Wahl ladeWahl(WahlPK wahlPK) {
    return wahlDAO.findByPrimaryKey(wahlPK);
  }

}
