
package de.ivu.elect.presentation.exchange;

import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.substringAfterLast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Optional;

import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;

import org.apache.deltaspike.core.api.scope.ConversationGroup;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.file.UploadedFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.datei.boundary.AbstractDateiImportSearchParameters;
import de.ivu.elect.business.datei.boundary.DateiImportEdit;
import de.ivu.elect.business.datei.boundary.DateiImportSearchParameters;
import de.ivu.elect.business.datei.control.FileExchange;
import de.ivu.elect.business.datei.control.ImportFileProperties;
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.FileTypeResolver;
import de.ivu.elect.business.datei.entity.ServerFileType;
import de.ivu.elect.business.exchange.control.AbstractImporter;
import de.ivu.elect.business.exchange.control.AbstractImporterExporter;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.control.ImportExportStreamCreator;
import de.ivu.elect.business.exchange.entity.DateiImportArt;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.jee.exception.ElectCommonException;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.faces.upload.UploadedFileWithCanonicalFilename;
import de.ivu.elect.jee.presentation.dynamicform.AdvancedDynaFormModel;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.ViewHavingFileUpload;
import de.ivu.elect.presentation.datei.DateiImportSearchView;
import de.ivu.elect.presentation.exchange.action.DateiImportFormatFestlegenAction;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;

/**
 * Backing Bean für Import-Seiten, die das Template <b>import-search.xhtml</b> benutzen. Bietet Funktionalität zur Dateiauswahl (<code>action="#{backingView.select}</code>) und Hochladen
 * (<code>value="#{backingView.download}"</code> ).
 */
@RequestScoped
@OnlineHilfeId(value = "364", mehrfachVergebenOk = true)
public abstract class AbstractImportSearchView<ImportObject, Context> extends AbstractImportExportView<Context> implements ImportExportViewMarker, ViewHavingFileUpload {

  private static final long serialVersionUID = 1L;

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

  @Inject
  @ConversationGroup(AbstractDateiImportSearchParameters.class)
  private transient DateiImportSearchParameters dateiImportSearchParameters;

  @View
  @Inject
  private transient DateiImportSearchView dateiImportSearchView;

  @Inject
  private transient ImportSearchAfterUploadRedirect importSearchAfterUploadRedirect;

  @Inject
  private transient DateiImportEdit dateiImportEdit;

  @Inject
  private transient DateiImportMetaForEntityConversationSelect dateiImportMetaForEntityConversationSelect;

  @Inject
  private transient ImportExportStreamCreator streamCreator;

  @Inject
  private transient FileExchange fileExchange;

  @Inject
  private transient ImportDescriptionSelect importDescriptionSelect;

  @Inject
  private transient DateiImportFormatFestlegenAction dateiImportFormatFestlegenAction;

  @Inject
  private transient FileTypeResolver fileTypeResolver;

  @Inject
  private transient ImportViewConfig importViewConfig;

  private List<DateiImport> searchResult = null;

  public AbstractImportSearchView() {
    super();
  }

  @PostConstruct
  public void initDescription() {
    importDescriptionSelect.setImportDescription(getImportDescription());
    dateiImportMetaForEntityConversationSelect.setMetaForEntity(getImporter().getMetaForEntity());
  }

  @Override
  protected ExchangeDescription getExchangeDescription() {
    return getImportDescription();
  }

  protected abstract ImportDescription getImportDescription();

  protected abstract AbstractImporter<ImportObject, Context, ? > getImporter();

  protected String getImportBestaetigenViewId() {
    return getImportDescription().getImportBestaetigenViewId();
  }

  protected String getImportFormatFestlegenViewId() {
    return getImportDescription().getImportFormatFestlegenViewId();
  }

  protected DateiImportArt getImport() {
    return getImportDescription().getDateiImportArt();
  }

  @Redirect
  public String commitComment() {
    dateiImportEdit.save(dateiImportSearchView.getSelectedEntity());
    addInfoMessage("datei.import.edit.action.kommentar.erfolgreich.gespeichert");
    return FacesContextUtil.getCurrentViewId();
  }

  public List<DateiImport> getSearchResult() {
    if (searchResult == null) {
      loadSearchResult();
    }
    return searchResult;
  }

  protected void loadSearchResult() {
    dateiImportSearchParameters.setImportDescription(getImportDescription());
    dateiImportSearchView.resetSearchResult();
    searchResult = dateiImportSearchView.getSearchResult();
  }

  @Redirect
  public String delete() {
    dateiImportEdit.delete(getSelectedEntity());
    addInfoMessage("datei.import.loeschen.erfolgreich");
    return FacesContextUtil.getCurrentViewId();
  }

  private AdvancedDynaFormModel model;

  public AdvancedDynaFormModel getModel() {
    if (model == null) {
      FormBuilder formBuilder = new FormBuilder();

      addProperties(formBuilder);

      model = formBuilder.createModel();
    }

    return model;
  }

  protected void addProperties(FormBuilder formBuilder) {
    // nichts zu tun
  }

  @Override
  public void handleFileUpload(FileUploadEvent event) {
    try {
      // ELECT-3038
      UploadedFile uploadedFile = new UploadedFileWithCanonicalFilename(event.getFile());
      String fileName = trimFileName(uploadedFile.getFileName());

      // ELECT-6103
      try {
        checkFileTypSupported(fileName);
      } catch (ElectCommonException e) {
        importSearchAfterUploadRedirect.setThrowable(e);
        return;
      }

      DateiImport dateiImport = createAndPersistDateiImportAndSpeichereDateiAufServer(event, fileName, getSupportedFileFormat(fileName));

      searchResult = null;

      addInfoMessage("datei.import.edit.action.upload.success.message", dateiImport.getDatei().getOriginalDateiname());

      // ELECT-1504 IE Sonderbehandlungen
      if (dateiImport.isFormatFestlegenMoeglich()) {
        importSearchAfterUploadRedirect.setRedirectToViewId(getImportFormatFestlegenViewId());
      } else if (dateiImport.isImportierenMoeglich()) {
        importSearchAfterUploadRedirect.setRedirectToViewId(getImportBestaetigenViewId());
      }

      // Aktionen für FormatFestlegen vorbereiten, aber KEIN(!) Redirect ELECT-1504
      dateiImportFormatFestlegenAction.doIt(dateiImport);

    } catch (Exception e) {
      LOG.warn(e.getMessage(), e);
      importSearchAfterUploadRedirect.setThrowable(e);
    }
  }

  @Override
  public String getFileUploadLabel() {
    return getMessage("datei.import.edit.action.upload.choose", getFileUploadFileTypesFormatted());
  }

  @Override
  public String getFileUploadAllowTypes() {
    return FILE_UPLOAD_ALLOW_TYPES_ALL; // ELECT-6103
  }

  @Override
  public String getFileUploadAccept() {
    return fileTypeResolver.getFileTypesHtmlAccept(getFileUploadFileTypes());
  }

  @Override
  public List<FileType> getFileUploadFileTypes() {
    return getSupportedFileTypes();
  }

  private String getFileUploadFileTypesFormatted() {
    return fileTypeResolver.formatFileTypes(getFileUploadFileTypes());
  }

  private void checkFileTypSupported(String fileName) {
    Optional<FileType> fileTypeOptional = fileTypeResolver.getFileType(fileName);

    if (fileTypeOptional.isPresent()) {
      FileType fileType = fileTypeOptional.get();

      for (FileFormat fileFormat : getSupportedFileFormats()) {
        if (fileFormat.getFileType().equals(fileType)) {
          return; // alles gut
        }
      }

      throw new ElectCommonException("entity.upload.action.upload.file.typ.not.supported", fileType.getFileExtension(), fileName, getFileUploadFileTypesFormatted());
    } else {
      throw new ElectCommonException("entity.upload.action.upload.file.typ.unknown", fileName, getFileUploadFileTypesFormatted());
    }
  }

  public static String trimFileName(String fileName) {
    String substringAfterFileSeparator = substringAfterLast(fileName, File.separator);

    if (isBlank(substringAfterFileSeparator)) {
      return fileName;
    }

    return substringAfterFileSeparator;
  }

  protected DateiImport createAndPersistDateiImportAndSpeichereDateiAufServer(FileUploadEvent event, String fileName, FileFormat fileFormat) throws IOException {

    ImportFileProperties importFileProperties = new ImportFileProperties(fileName, fileFormat, getImport());

    if (getSelectedWahl() == null) {
      return fileExchange.createAndPersistDateiImportAndSpeichereDateiAufServer(event.getFile().getInputStream(), importFileProperties, bestimmeMandant(), getSelectedWahltermin(),
          createNewDateiImportBerichtCustomData(), ServerFileType.IMPORTIERTE_DATEI);
    } else {
      return fileExchange.createAndPersistDateiImportAndSpeichereDateiAufServer(event.getFile().getInputStream(), importFileProperties, bestimmeMandant(), getSelectedWahl(),
          createNewDateiImportBerichtCustomData(), ServerFileType.IMPORTIERTE_DATEI);
    }
  }

  private Mandant bestimmeMandant() {
    Wahl selectedWahl = getSelectedWahl();
    if (selectedWahl != null) {
      return selectedWahl.getMandant();
    }

    Wahltermin selectedWahltermin = getSelectedWahltermin();
    if (selectedWahltermin != null) {
      return selectedWahltermin.getMandant();
    }

    return getMandant();

  }

  private FileFormat getSupportedFileFormat(String fileName) {
    List<FileFormat> supportedFileFormats = getSupportedFileFormats();
    FileFormat defaultFileFormat = supportedFileFormats.get(0);

    if (supportedFileFormats.size() == 1) {
      return defaultFileFormat;
    } else {
      return getSupportedFileFormatOrDefault(fileName, supportedFileFormats, defaultFileFormat);
    }
  }

  private FileFormat getSupportedFileFormatOrDefault(String fileName, List<FileFormat> supportedFileFormats, FileFormat defaultFileFormat) {
    if (isBlank(fileName)) {
      throw new IllegalArgumentException("Der übergebene Dateiname war leer oder null!");
    }

    Optional<FileType> fileTypeOptional = fileTypeResolver.getFileType(fileName);

    if (fileTypeOptional.isPresent()) {
      FileType fileType = fileTypeOptional.get();
      // NOTE, dass wir hiermit das erstbeste passende FileFormat nehmen und dabei möglicherweise andere überspringen!
      return supportedFileFormats.stream().filter(fileFormat -> fileType.equals(fileFormat.getFileType())).findFirst().orElse(defaultFileFormat);
    }

    return defaultFileFormat;
  }

  public StreamedContent getDownload() throws FileNotFoundException {
    return dateiImportSearchView.getSelectedEntity().getDatei().getDefaultStreamedContent(streamCreator);
  }

  public String getViewId() {
    return FacesContextUtil.getCurrentViewId();
  }

  public boolean isEntitySelected() {
    return dateiImportSearchView.isEntitySelected();
  }

  public DateiImport getSelectedEntity() {
    return dateiImportSearchView.getSelectedEntity();
  }

  protected abstract DateiImportBerichtCustomData createNewDateiImportBerichtCustomData();

  @Override
  protected AbstractImporterExporter<Context> getImporterExporter() {
    return getImporter();
  }

  /**
   * Überschreiben, wenn es Situationen gibt, in denen ein Import nicht zulässig ist
   *
   * @return
   */
  public boolean isUploadPossible() {
    return true;
  }

  public String getUploadNotPossibleMessageKey() {
    return "";
  }

  public boolean isSearchRendered() {
    return importViewConfig.isSearchRendered();
  }
}
