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

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;

import javax.annotation.Nullable;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

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.DateiFileNames;
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.datei.entity.ServerFileType;
import de.ivu.elect.business.datei.entity.WrappedFileDateiFileNames;
import de.ivu.elect.business.exchange.control.FileFormat;
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.exchange.entity.DateiImportArtFileNames;
import de.ivu.elect.business.exchange.entity.ImportStatus;
import de.ivu.elect.business.mandant.boundary.MandantDAO;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.wahl.boundary.WahlterminDAO;
import de.ivu.elect.business.wahl.control.WahlAttacher;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.jee.faces.Messages;

@Stateless
public class FileExchange {

  @Inject
  protected transient Messages messages;

  @Inject
  protected transient WahlAttacher wahlDAO;

  @Inject
  protected transient WahlterminDAO wahlterminDAO;

  @Inject
  protected transient MandantDAO mandantDAO;

  @Inject
  protected transient DateiDAO dateiDAO;

  @Inject
  protected transient DateiExportDAO dateiExportDAO;

  @Inject
  protected transient DateiImportDAO dateiImportDAO;

  @Inject
  protected transient ImportExportStreamCreator streamCreator;

  @Inject
  protected transient FileExchangeDirectory fileExchangeDirectory;

  public FileExchange() {
    super();
  }

  /**
   * Wird verwendet für den Upload von Dateien
   */
  public DateiImport createAndPersistDateiImportAndSpeichereDateiAufServer(InputStream inputStream, ImportFileProperties importFileProperties, Mandant mandantNotAttached, Wahl wahlNotAttached,
      DateiImportBerichtCustomData dateiImportBerichtCustomData, ServerFileType serverFileType) throws IOException {
    Wahl wahl = attachWahl(wahlNotAttached);

    Wahltermin wahltermin = null;

    if (wahl != null) {
      wahltermin = wahl.getWahltermin();
    }

    DateiImportArt dateiImportArt = importFileProperties.getDateiArt();
    FileFormat fileFormat = importFileProperties.getFileFormat();

    File file = speichereDateiAufServer(inputStream, fileFormat, dateiImportArt, wahltermin, wahl, serverFileType);

    DateiFileNames dateiFileNames = new DateiImportArtFileNames(messages, dateiImportArt, importFileProperties.getDateiname(), file.getName());

    return createDateiImportAndDatei(dateiImportArt, fileFormat, dateiFileNames, wahl, wahltermin, mandantNotAttached, dateiImportBerichtCustomData, serverFileType);
  }

  public DateiImport createAndPersistDateiImportAndSpeichereDateiAufServer(InputStream inputStream, ImportFileProperties importFileProperties, Mandant mandantNotAttached,
      Wahltermin wahlterminNotAttached, DateiImportBerichtCustomData dateiImportBerichtCustomData, ServerFileType serverFileType) throws IOException {
    Wahltermin wahltermin = attachWahltermin(wahlterminNotAttached);

    DateiImportArt dateiImportArt = importFileProperties.getDateiArt();
    FileFormat fileFormat = importFileProperties.getFileFormat();

    File file = speichereDateiAufServer(inputStream, fileFormat, dateiImportArt, wahltermin, null, serverFileType);

    DateiFileNames dateiFileNames = new DateiImportArtFileNames(messages, dateiImportArt, importFileProperties.getDateiname(), file.getName());

    return createDateiImportAndDatei(dateiImportArt, fileFormat, dateiFileNames, null, wahltermin, mandantNotAttached, dateiImportBerichtCustomData, serverFileType);
  }

  private Wahl attachWahl(Wahl wahl) {
    return wahlDAO.attach(wahl);
  }

  @Nullable
  private Wahltermin attachWahltermin(Wahltermin wahltermin) {
    if (wahltermin == null) {
      return null;
    }
    return wahlterminDAO.findByPrimaryKey(wahltermin.getPk());
  }

  /**
   * Wird verwendet für den Export von Dateien
   *
   * @param wahl
   */
  public DateiExport createAndPersistDateiExport(DateiExportArt dateiExportArt, FileFormat fileFormat, DateiFileNames dateiFileNames, Mandant mandant, Wahl wahl, ServerFileType serverFileType) {
    DateiExport dateiExport = createDateiExport(dateiExportArt, fileFormat, dateiFileNames, mandant, wahl, null, serverFileType);

    dateiDAO.persist(dateiExport.getDatei());
    dateiExportDAO.persist(dateiExport);

    return dateiExport;
  }

  public DateiExport createAndPersistDateiExport(DateiExportArt dateiExportArt, FileFormat fileFormat, DateiFileNames dateiFileNames, Mandant mandant, Wahl wahl, Wahltermin wahltermin,
      ServerFileType serverFileType) {
    DateiExport dateiExport = createDateiExport(dateiExportArt, fileFormat, dateiFileNames, mandant, wahl, wahltermin, serverFileType);

    dateiDAO.persist(dateiExport.getDatei());
    dateiExportDAO.persist(dateiExport);

    return dateiExport;
  }

  public DateiExport createDateiExport(DateiExportArt dateiExportArt, FileFormat fileFormat, DateiFileNames dateiFileNames, Mandant mandantNotAttached, Wahl wahlNotAttached,
      Wahltermin wahlterminNotAttached, ServerFileType serverFileType) {
    Mandant mandant = attachMandant(mandantNotAttached);

    Wahl wahl = attachWahl(wahlNotAttached);

    Wahltermin wahltermin = attachWahltermin(wahlterminNotAttached);

    Datei datei = createDatei(fileFormat, dateiFileNames, wahl, wahltermin, mandant, serverFileType);

    return createDateiExport(dateiExportArt, datei);
  }

  private Mandant attachMandant(Mandant mandantNotAttached) {
    return mandantDAO.attach(mandantNotAttached);
  }

  public Datei speichereDateiAufServerUndCreateDatei(InputStream inputStream, FileFormat fileFormat, DateiFileNames dateiFileNames, Mandant mandant, @Nullable Wahltermin wahltermin,
      @Nullable Wahl wahl, ServerFileType serverFileType) throws IOException {
    File fileOnServer = speichereDateiAufServer(inputStream, fileFormat, wahltermin, wahl, serverFileType, dateiFileNames);

    // ELECTKOM-1603
    WrappedFileDateiFileNames wrappedFileDateiFileNames = new WrappedFileDateiFileNames(dateiFileNames, fileOnServer);

    return createDatei(fileFormat, wrappedFileDateiFileNames, wahl, wahltermin, mandant, serverFileType);
  }

  private File speichereDateiAufServer(InputStream inputStream, FileFormat fileFormat, DateiImportArt dateiImportArt, @Nullable Wahltermin wahltermin, @Nullable Wahl wahl,
      ServerFileType serverFileType) throws IOException {
    return speichereDateiAufServer(inputStream, fileFormat, wahltermin, wahl, serverFileType, new DateiImportArtFileNames(messages, dateiImportArt));
  }

  private File speichereDateiAufServer(InputStream inputStream, FileFormat fileFormat, Wahltermin wahltermin, Wahl wahl, ServerFileType serverFileType, DateiFileNames dateiFileNames)
      throws IOException {
    String dateiname = dateiFileNames.getDateiname(fileFormat, wahl, wahltermin);

    try (OutputStream outputStream = streamCreator.createOutputStreamForDatei(dateiname, fileExchangeDirectory.erstellePfad(wahltermin, wahl, serverFileType))) {
      copyInputStreamToOutputStream(inputStream, outputStream);
    }

    return new File(dateiname);
  }

  public void copyInputStreamToOutputStream(InputStream inputStream, OutputStream outputStream) throws IOException {
    try {
      org.apache.commons.io.IOUtils.copy(inputStream, outputStream);
    } catch (IOException e) {
      throw new IOException(e.getMessage(), e);
    }
  }

  private DateiImport createDateiImportAndDatei(DateiImportArt dateiImportArt, FileFormat fileFormat, DateiFileNames dateiFileNames, Wahl wahl, Wahltermin wahltermin, Mandant mandant,
      DateiImportBerichtCustomData dateiImportBerichtCustomData, ServerFileType serverFileType) {
    return createAndPersistDateiImport(dateiImportArt, createDatei(fileFormat, dateiFileNames, wahl, wahltermin, mandant, serverFileType), dateiImportBerichtCustomData);
  }

  private Datei createDatei(FileFormat fileFormat, DateiFileNames dateiFileNames, Wahl wahl, Wahltermin wahltermin, Mandant mandant, ServerFileType serverFileType) {
    FileType fileType = fileFormat.getFileType();
    Datei datei = fileType.createDatei();
    datei.setErstellungszeitpunkt(new Date());
    datei.setOriginalDateiname(dateiFileNames.getOriginalDateiname(fileFormat, wahl, wahltermin));
    datei.setFileType(fileType);
    datei.setMimeType(fileType.getDefaultMimeType());
    datei.setFormat(fileFormat.getName());
    datei.setDateiname(dateiFileNames.getDateiname(fileFormat, wahl, wahltermin));
    datei.setMandant(attachMandant(mandant));
    if (wahl != null) {
      datei.setWahl(attachWahl(wahl));
    }
    if (wahltermin != null) {
      datei.setWahltermin(attachWahltermin(wahltermin));
    }

    datei.setPfad(fileExchangeDirectory.erstellePfad(datei, serverFileType));

    return datei;
  }

  private DateiImport createAndPersistDateiImport(DateiImportArt dateiArt, Datei datei, DateiImportBerichtCustomData dateiImportBerichtCustomData) {
    DateiImport dateiImport = new DateiImport(dateiImportBerichtCustomData);
    dateiImport.setDateiArt(dateiArt);
    dateiImport.setDatei(datei);
    dateiImport.setImportiertAm(new Date());
    dateiImport.setImportStatus(datei.getFileType().getDefaultUploadStatus());
    Datei dateiToPersist = dateiImport.getDatei();
    dateiDAO.persist(dateiToPersist);
    dateiImportDAO.persist(dateiImport);

    return dateiImport;
  }

  private DateiExport createDateiExport(DateiExportArt exporte, Datei datei) {
    DateiExport dateiExport = new DateiExport();
    dateiExport.setDateiArt(exporte);
    dateiExport.setDatei(datei);
    return dateiExport;
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void dateiAblehnen(DateiImport dateiImport, String kommentar) {
    DateiImport attachedDateiImport = dateiImportDAO.attach(dateiImport);
    attachedDateiImport.setImportStatus(ImportStatus.ABGELEHNT);
    attachedDateiImport.setKommentar(kommentar);
  }
}
