
package de.ivu.elect.presentation.exchange;

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

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.inject.Inject;

import org.apache.deltaspike.core.api.scope.ViewAccessScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.datei.boundary.DateiExportConversationSelect;
import de.ivu.elect.business.datei.boundary.DateiExportEdit;
import de.ivu.elect.business.datei.boundary.DateiExportSearchParameters;
import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.datei.entity.DateiConstants;
import de.ivu.elect.business.datei.entity.DateiExport;
import de.ivu.elect.business.datei.entity.DateiExportPK;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.dokumente.boundary.DateiKonverter;
import de.ivu.elect.business.exchange.control.AbstractExchangeContext;
import de.ivu.elect.business.exchange.control.AbstractExporter;
import de.ivu.elect.business.exchange.control.AbstractImporterExporter;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.entity.DateiExportArt;
import de.ivu.elect.business.gebietsbaum.boundary.GebietSelectChangeEvent;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.wahl.boundary.WahlSelectChangeEvent;
import de.ivu.elect.business.wahl.boundary.WahlterminSelectChangeEvent;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterExtendedLoader;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.faces.PreConditionCheck;
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.datei.DateiExportSearchView;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;
import de.ivu.elect.util.ThrowableUtil;

@ViewAccessScoped
@OnlineHilfeId(value = "213", mehrfachVergebenOk = true)
public abstract class AbstractExportSearchView<ExportObject, C> extends AbstractImportExportView<C> implements Serializable, ImportExportViewMarker, ExportView<C> {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient DateiExportEdit dateiExportEdit;

  @Inject
  private transient DateiExportConversationSelect dateiExportConversationSelect;

  @View
  @Inject
  private transient DateiExportSearchView dateiExportSearchView;

  @Inject
  private transient ExportDescriptionSelect exportDescriptionSelect;

  @Inject
  private transient DateiKonverter dateiKonverter;

  @Inject
  private transient WahlParameterExtendedLoader wahlParameterExtendedLoader;

  private final Logger log = LoggerFactory.getLogger(getClass());

  private final Map<String, Object> customParameterMap = new HashMap<>();

  private List<DateiExport> searchResult = null;

  private AdvancedDynaFormModel exportParameterModel;

  private FileFormat exportParameterModelFileFormat;

  private ExportViewConfirmDialog exportConfirmDialog;

  protected AbstractExportSearchView() {
    super();
  }

  @Override
  public UseCasePermission getUseCasePermission() {
    return getExportDescription().getUseCasePermission();
  }

  @Override
  @Nullable
  @PreConditionCheck
  public String checkPreCondition() {
    String outcome = super.checkPreCondition();

    if (isBlank(outcome)) {
      exportDescriptionSelect.setExportDescription(getExportDescription());

      preSelectFileFormat(getSupportedFileFormats());
    }

    return outcome;
  }

  @PreDestroy
  public void cleanup() {
    reset(); // ELECTVAPP-1135
  }

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

  public abstract ExportDescription getExportDescription();

  /**
   * Kundenspezifische Parameter...
   *
   * @deprecated
   * @see AbstractExportSearchView#addProperties(FormBuilder, FileFormat, Object)
   */
  @Deprecated
  protected Map<String, Object> getCustomParameterMap() {
    return customParameterMap;
  }

  @Override
  public boolean isSupportedFileFormatsRendered() {
    return super.isSupportedFileFormatsRendered() && isBtnExportierenRendered();
  }

  protected void preSelectFileFormat(List<FileFormat> supportedFileFormats) {
    if (supportedFileFormats.size() == 1) {
      setSelectedFileFormat(supportedFileFormats.get(0));
    }
  }

  @Override
  @PostConstruct
  public void initFileFormat() {
    exportDescriptionSelect.setExportDescription(getExportDescription());

    super.initFileFormat();
  }

  @Override
  public boolean isSearchResultEntrySelected() {
    return isEntitySelected();
  }

  @Override
  public DateiExport getSelectedSearchResultEntry() {
    return getSelectedEntityCheckNull();
  }

  @Override
  public void setSelectedSearchResultEntry(DateiExport dateiExport) {
    setSelectedEntity(dateiExport);
  }

  @Override
  public boolean isSearchResultRendered() {
    return true;
  }

  @Override
  public List<DateiExport> getSearchResult() {
    if (searchResult == null) {
      searchResult = loadSearchResult();
    }

    return searchResult;
  }

  private void resetSearchResult() {
    searchResult = null;
  }

  private List<DateiExport> loadSearchResult() {
    DateiExportSearchParameters dateiExportSearchParameters = dateiExportSearchView.getSearchParameters();
    dateiExportSearchParameters.reset();

    return loadSearchResult(dateiExportSearchParameters);
  }

  protected List<DateiExport> loadSearchResult(DateiExportSearchParameters dateiExportSearchParameters) {
    dateiExportSearchParameters.setExportDescription(getExportDescription()); // Immer setzen...

    dateiExportSearchView.resetSearchResult();

    return dateiExportSearchView.getSearchResult();
  }

  public boolean isEntitySelected() {
    return dateiExportConversationSelect.isSelected();
  }

  // ELECTMUC-982
  public DateiExport getSelectedEntityCheckNull() {
    DateiExport entity = getSelectedEntity();

    if (entity == null) {
      clearSelectedEntity();

      return DateiExport.DUMMY;
    }

    return entity;
  }

  public DateiExport getSelectedEntity() {
    return dateiExportConversationSelect.getEntity();
  }

  public void setSelectedEntity(DateiExport dateiExport) {
    dateiExportConversationSelect.setEntity(dateiExport);
  }

  protected void clearSelectedEntity() {
    dateiExportConversationSelect.clearEntity();
    customParameterMap.clear();
  }

  public boolean isBtnCancelRendered() {
    return false;
  }

  @Redirect
  public String cancel() {
    reset();

    return FacesContextUtil.getCurrentViewId();
  }

  public boolean isNeedsDownload() {
    DateiExport dateiExport = getSelectedEntity();

    if (dateiExport != null) {
      Datei datei = dateiExport.getDatei();

      return datei != null && getSupportedFileFormats().contains(datei.getFileFormat()); // ELECT-11436
    }

    return false;
  }

  @Override
  public boolean isBtnDeleteRendered() {
    return true;
  }

  @Redirect
  @Override
  public String delete() {
    callDelete(getSelectedEntity());

    clearSelectedEntity();

    addInfoMessage("entity.edit.action.delete.success.message", getMessage(DateiConstants.MESSAGE_KEY_ENTITY_EINZAHL));

    resetSearchResult();

    return FacesContextUtil.getCurrentViewId();
  }

  protected void callDelete(DateiExport dateiExport) {
    dateiExportEdit.delete(dateiExport);
  }

  public boolean isBtnExportierenDisabled() {
    return false;
  }

  public boolean isBtnExportierenRendered() {
    return true;
  }

  @Redirect
  public String exportieren() {
    boolean confirmed = getExportConfirmDialog() == null || !getExportConfirmDialog().isRequired();

    if (confirmed) {
      return exportierenConfirmed();
    }

    return FacesContextUtil.getCurrentViewId();
  }

  @Redirect
  public String exportierenConfirmed() {
    resetExportConfirmDialog();

    export(getSelectedFileFormat(), getContext(), null);

    return FacesContextUtil.getCurrentViewId();
  }

  @Override
  public boolean isSupportsFinaleKonvertierung(FileType fileType) {
    return fileType != null;
  }

  @Override // Cf. AbstractDokumentErzeugenView#export!!!
  public ExportViewResult export(FileFormat fileFormat, C context, @Nullable FileType finaleKonvertierung) {
    String viewId = getExportDescription().getExportSearchViewId();
    DateiExportArt dateiExportArt = getExportDescription().getDateiExportArt();

    FileFormat fileFormatExported = fileFormat;
    boolean finaleKonvertierungSupported = isSupportsFinaleKonvertierung(finaleKonvertierung);
    boolean finaleKonvertierungRequired = finaleKonvertierungSupported && !Objects.equals(finaleKonvertierung, fileFormatExported.getFileType());

    log.info("Export - start [view = " + viewId + ", art = " + dateiExportArt + ", format = " + fileFormat + ", konvertierung = " + finaleKonvertierung + ", konvertierung unterstuetzt = "
        + finaleKonvertierungSupported + ", konvertierung erforderlich = " + finaleKonvertierungRequired + "]");

    setSelectedFileFormat(fileFormatExported);

    DateiExportPK dateiExportPk = null;

    try {
      log.info("\tGenerierung - start");

      if (isExportSpecificPerWahl()) {
        dateiExportPk = exportAndDisplayMessage(getSelectedWahl(), getSelectedWahltermin(), context);
      } else if (isExportSpecificPerWahltermin()) {
        dateiExportPk = exportAndDisplayMessage(null, getSelectedWahltermin(), context);
      } else {
        dateiExportPk = exportAndDisplayMessage(null, null, context);
      }

      log.info("\tGenerierung - done [dateiExport = " + dateiExportPk + "]");

      if (finaleKonvertierungRequired && dateiExportPk != null) {
        log.info("\tKonvertierung - start [fileType = " + finaleKonvertierung + "]");

        dateiExportPk = dateiKonverter.konvertiere(dateiExportPk, finaleKonvertierung);

        addInfoMessage("datei.export.action.konvertieren.success.message", getMessage(finaleKonvertierung));

        log.info("\tKonvertierung - done [dateiExport = " + dateiExportPk + ", fileType = " + finaleKonvertierung + "]");
      } else {
        addSuccessMessage();
      }
    } catch (Exception ex) {
      log.warn("Export - fehlgeschlagen [view = " + viewId + ", art = " + dateiExportArt + ", format = " + fileFormat + ", konvertierung = " + finaleKonvertierung + "]", ex);

      addErrorMessage("datei.export.action.exportieren.error.message", ThrowableUtil.getMessageDerUrsache(ex));

      return Optional::empty;
    }

    if (dateiExportPk == null) {
      clearSelectedEntity();
    } else {
      dateiExportConversationSelect.setEntity(dateiExportPk);

      if (context instanceof AbstractExchangeContext) {
        AbstractExchangeContext exchangeContext = (AbstractExchangeContext) context;

        if (exchangeContext.hasPostExportTargetDirectory()) {
          // Keinen Download-Dialog anbieten. Datei liegt auf dem Server, Überschreiben von AbstractExportSearchView#isNeedsDownload ist dafür nicht ausreichend!
          clearSelectedEntity();

          addInfoMessage("datei.export.action.exportieren.copied.to.server.message", exchangeContext.getPostExportTargetDirectory().getAbsolutePath());
        }
      }
    }

    resetSearchResult();

    log.info("Export - done [dateiExport = " + dateiExportPk + "]");

    if (getSelectedEntity() == null) {
      return Optional::empty;
    }

    return () -> Optional.ofNullable(getSelectedEntity().getPk());
  }

  private DateiExportPK exportAndDisplayMessage(Wahl wahl, Wahltermin wahltermin, C context) {
    Mandant mandant = null; // ELECT-8038

    if (wahl != null) {
      mandant = wahl.getMandant();
    }

    if (wahltermin != null) {
      mandant = wahltermin.getMandant();
    }

    if (mandant == null) {
      mandant = getMandant();
    }

    return getExporter().export(getSelectedFileFormat(), context, mandant, wahl, wahltermin);
  }

  protected void addSuccessMessage() {
    addInfoMessage("datei.export.action.exportieren.success.message");
  }

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

  @Override
  protected AbstractImporterExporter<C> getImporterExporter() {
    return getExporter();
  }

  protected abstract AbstractExporter<ExportObject, C> getExporter();

  public boolean isExportParameterModelPresent() {
    return getExportParameterModel().isAnyPropertyAdded();
  }

  public AdvancedDynaFormModel getExportParameterModel() {
    if (!isExportParameterModelCreated() || isExportParameterModelDynamic() && !Objects.equals(getSelectedFileFormat(), exportParameterModelFileFormat)) {
      exportParameterModel = createExportParameterModel();
    }

    return exportParameterModel;
  }

  protected boolean isExportParameterModelCreated() {
    return exportParameterModel != null;
  }

  /**
   * @deprecated POC für ELECTKOM-480, Hilfsmethode, um nicht ALLE Exporte zu crashen...
   * @see AuszahlungExportView#isExportParameterModelDynamic
   */
  @Deprecated
  protected boolean isExportParameterModelDynamic() {
    return false;
  }

  private AdvancedDynaFormModel createExportParameterModel() {
    FormBuilder formBuilder = new FormBuilder();

    FileFormat fileFormat = getSelectedFileFormat();

    addProperties(formBuilder);
    addProperties(formBuilder, fileFormat, getContext());

    exportParameterModelFileFormat = fileFormat;

    return formBuilder.createModel();
  }

  protected void resetExportParameterModel() {
    exportParameterModel = null;
    exportParameterModelFileFormat = null;
  }

  /**
   * @deprecated
   * @see AbstractExportSearchView#addProperties(FormBuilder, FileFormat, Object)
   */
  @Deprecated
  protected void addProperties(FormBuilder formBuilder) {
    // Do nothing
  }

  protected void addProperties(FormBuilder formBuilder, @Nullable FileFormat fileFormat, C context) {
    getExporter().addProperties(formBuilder, fileFormat, context);
  }

  public ExportViewConfirmDialog getExportConfirmDialog() {
    if (exportConfirmDialog == null) {
      exportConfirmDialog = ExportViewConfirmDialog.UNSUPPORTED;
    }

    return exportConfirmDialog;
  }

  protected void setExportConfirmDialog(ExportViewConfirmDialog exportConfirmDialog) {
    this.exportConfirmDialog = exportConfirmDialog;
  }

  protected void resetExportConfirmDialog() {
    exportConfirmDialog = null;
  }

  // ELECT-6400.
  public void onWahlSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) WahlSelectChangeEvent event) {
    if (isExportSpecificPerWahl()) {
      reset();
    }
  }

  // ELECT-6400.
  public void onWahlterminSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) WahlterminSelectChangeEvent event) {
    if (isExportSpecificPerWahltermin()) {
      reset();
    }
  }

  public void onGebietSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) GebietSelectChangeEvent event) {
    reset();
  }

  /**
   * in abgeleiteten Klassen überschreiben, wenn es etwas aufzuräumen gibt
   */
  protected void reset() {
    clearSelectedEntity();

    resetSearchResult();

    resetExportParameterModel();
    resetExportConfirmDialog();
  }

  protected boolean isExportSpecificPerWahl() {
    return getExportDescription().isSpecificPerWahl();
  }

  protected boolean isExportSpecificPerWahltermin() {
    return getExportDescription().isSpecificPerWahltermin();
  }

  protected WahlParameterExtendedLoader getWahlParameterLoader() {
    return wahlParameterExtendedLoader;
  }
}
