
package de.ivu.elect.presentation.exchange;

import static java.util.Arrays.asList;
import static org.apache.commons.lang3.StringUtils.isBlank;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

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

import org.apache.deltaspike.core.api.scope.ViewAccessScoped;
import org.primefaces.model.chart.PieChartModel;

import de.ivu.elect.business.datei.boundary.DateiImportConversationSelect;
import de.ivu.elect.business.datei.boundary.DateiImportEdit;
import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.datei.entity.DateiImport;
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.entity.ImportStatus;
import de.ivu.elect.business.exchange.entity.ImportUpdateReport;
import de.ivu.elect.jee.entity.Meta;
import de.ivu.elect.jee.exception.ElectCommonException;
import de.ivu.elect.jee.exchange.AbstractViolation;
import de.ivu.elect.jee.exchange.ImportOptions;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.faces.PreConditionCheck;
import de.ivu.elect.jee.presentation.AjaxHelper;
import de.ivu.elect.jee.presentation.charts.PieChartModelBuilder;
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.exchange.action.DateiImportFormatFestlegenAction;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;

@OnlineHilfeId(value = "236", mehrfachVergebenOk = true)
@ViewAccessScoped
public abstract class AbstractImportBestaetigenView<ImportObject, Context> extends AbstractImportExportView<Context> implements Serializable {

  private static final long serialVersionUID = 1L;

  private static final int DEFAULT_SHOWN_VIOLATIONS = 100;

  @Inject
  protected transient DateiImportConversationSelect dateiImportSelect;

  @Inject
  protected transient DateiImportEdit dateiImportEdit;

  @Inject
  protected transient DateiImportFormatFestlegenAction dateiImportFormatFestlegenAction;

  @Inject
  protected transient ViolationsBeimImportAnzeigen violationsBeimImportAnzeigen;

  @Inject
  protected transient FileFormatFormatter fileFormatFormatter;

  @Inject
  private transient ImportViewConfig importViewConfig;

  private ImportOptions importOptions;

  private List<ImportOptions> importOptionsAuswahl;

  private ImportUpdateReport importUpdateReport;

  private PieChartModel pieChartModel;

  private boolean refreshRequired;

  private List<AbstractViolation> violations;

  private AdvancedDynaFormModel model;

  protected AbstractImportBestaetigenView() {
    super();
  }

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

    if (isBlank(outcome)) {
      DateiImport dateiImport = getSelectedEntity();

      if (dateiImport != null && dateiImport.isImportierenMoeglich()) {
        return null;
      }

      return getSearchViewId();
    }

    return outcome;
  }

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

      addProperties(formBuilder, getSelectedEntity(), getContext());

      model = formBuilder.createModel();
    }

    return model;
  }

  protected void addProperties(FormBuilder formBuilder, DateiImport dateiImport, Context context) {
    getImporter().addProperties(formBuilder, dateiImport, context);
  }

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

  @Override
  protected abstract Context getContext();

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

  protected abstract ImportDescription getImportDescription();

  protected String getSearchViewId() {
    return importViewConfig.getSearchViewId(getImportDescription().getImportSearchViewId());
  }

  public boolean isConfirmationForImportNeeded() {
    return getImportDescription().isConfirmationForImportNeeded() && isWerdenDatenInDatenbankGeloeschtOderAktualisiert();
  }

  private boolean isWerdenDatenInDatenbankGeloeschtOderAktualisiert() {
    return getImportReport().getNurInDatenbank() > 0 && getImportOptions().isLoescheBestehendenDatenWennNichtInImportDatenVorhanden()
        || getImportReport().getInDateiUndInDatenbank() > 0 && getImportOptions().isAktualisiereBestehendenDaten();
  }

  public void checkImportReportContainsViolations() {
    if (isImportReportContainingViolations()) {
      String sectionHeader = getMessage("datei.import.bestaetigen.import.report.legend.errors");

      if (isImportReportContainingErrors()) {
        addErrorMessage("datei.import.import.violations.importieren.nicht.moeglich", sectionHeader);
      } else {
        addWarningMessage("datei.import.import.violations.importieren.moeglich", sectionHeader);
      }
    }

    ajaxHelper.update(AjaxHelper.GLOBAL_MESSAGES_ID);

  }

  public boolean isRefreshRequired() {
    return refreshRequired && isRefreshRendered();
  }

  public void setRefreshRequired(boolean refreshRequired) {
    this.refreshRequired = refreshRequired;
  }

  @Redirect
  public String refresh() {
    importUpdateReport = null;
    pieChartModel = null;
    violations = null;
    setRefreshRequired(false);

    return FacesContextUtil.getCurrentViewId();
  }

  public boolean isBtnImportierenDisabled() {
    return isRefreshRequired() /* || ELECTKOM-198 elect unnötig, den Import-Button zu sperren, der Importer sagt sowieso "nein" isImportReportContainingErrors() */;
  }

  @Redirect
  public String importieren() {
    try {
      getImporter().importieren(getSelectedEntity(), getContext(), getImportOptions());
    } catch (ElectCommonException ex) {
      if (!isRefreshRendered()) {
        importUpdateReport = null;
        pieChartModel = null;
        violations = null;
        setRefreshRequired(false);
      }

      throw ex;
    } catch (Exception ex) {
      if (!isRefreshRendered()) {
        importUpdateReport = null;
        pieChartModel = null;
        violations = null;
        setRefreshRequired(false);
      }

      throw new ElectCommonException("importer.importieren.fehlgeschlagen.fehler.vorhanden", ex, ex.getMessage());
    }

    DateiImport dateiImport = getSelectedEntity();

    if (dateiImport != null && dateiImport.getDatei() != null) {
      addInfoMessage("datei.import.edit.action.import.success.message", dateiImport.getDatei().getOriginalDateiname());
    }

    return getImportErfolgtViewId();
  }

  protected String getImportErfolgtViewId() {
    return importViewConfig.getImportErfolgtViewId();
  }

  @Redirect
  public String cancel() {
    return getSearchViewId();
  }

  @Redirect
  public String importFormatFestlegen() {
    return dateiImportFormatFestlegenAction.doIt(getSelectedEntity());
  }

  public boolean isEntitySelected() {
    return getSelectedEntity() != null;
  }

  @Redirect
  public String ablehnen() {
    DateiImport dateiImport = getSelectedEntity();
    dateiImport.setImportStatus(ImportStatus.ABGELEHNT);

    dateiImportEdit.save(dateiImport);

    return getSearchViewId();
  }

  public boolean isCsvDatei() {
    return getSelectedEntity().getDatei().isCsvDatei();
  }

  public DateiImport getSelectedEntity() {
    return dateiImportSelect.getEntity();
  }

  public boolean isBtnEditFormatFestlegenRendered() {
    return !isNonCsvAndOnlyOneFormat(getSelectedEntity());
  }

  protected boolean isImportReportContainingViolations() {
    return !getImportReportViolations().isEmpty();
  }

  protected boolean isImportReportContainingErrors() {
    return getImportReportViolations().containsViolationsWithLevelError();
  }

  private Violations getImportReportViolations() {
    return getImportReport().getViolations();
  }

  public ImportUpdateReport getImportReport() {
    if (importUpdateReport == null) {
      importUpdateReport = getImporter().getImportUpdateReport(getSelectedEntity(), getContext(), getImportOptions());
    }

    return importUpdateReport;
  }

  public Meta getMetaForEntity() {
    return getImporter().getMetaForEntity();
  }

  public List<AbstractViolation> getViolations() {
    if (violations == null) {
      // HINT: Kopien erzeugen, um Sortierbarkeit an der Oberfläche zu gewährleisten
      if (violationsBeimImportAnzeigen.isRestrictMaxCountOfViolations()) {
        violations = new ArrayList<>(getImportReportViolations().getViolationsMaxCount(DEFAULT_SHOWN_VIOLATIONS));
      } else {
        violations = new ArrayList<>(getImportReportViolations().getViolations());
      }
    }

    return violations;
  }

  public String getErrorStyleClass() {
    for (AbstractViolation abstractViolation : getViolations()) {
      if (abstractViolation.getViolationLevel().isError()) {
        return "error";
      }
    }

    for (AbstractViolation abstractViolation : getViolations()) {
      if (abstractViolation.getViolationLevel().isWarning()) {
        return "warning";
      }
    }

    return "";
  }

  public ImportOptions getImportOptions() {
    if (importOptions == null) {
      importOptions = findDefaultImportOptions(getContext());
    }

    return importOptions;
  }

  public void setImportOptions(ImportOptions importOptions) {
    this.importOptions = importOptions;
  }

  private ImportOptions findDefaultImportOptions(Context context) {
    ImportOptions defaultImportOptions = getImportDescription().getDefaultImportOptions();

    Collection<ImportOptions> supportedImportOptions = getSupportedImportOptions(context);

    if (!supportedImportOptions.isEmpty() && !supportedImportOptions.contains(defaultImportOptions)) {
      return supportedImportOptions.iterator().next();
    }

    return defaultImportOptions;
  }

  public List<ImportOptions> getImportOptionsAuswahl() {
    if (importOptionsAuswahl == null) {
      importOptionsAuswahl = collectImportOptionsAuswahl(getContext());
    }

    return importOptionsAuswahl;
  }

  public void setImportOptionsAuswahl(List<ImportOptions> importOptionsAuswahl) {
    this.importOptionsAuswahl = importOptionsAuswahl;
  }

  private List<ImportOptions> collectImportOptionsAuswahl(Context context) {
    // Zur Auswahl sollen die ImportOptions stehen, die Aktionen umfassen, die auch vom Importer unterstützt werden.
    Set<ImportOptions> importOptionsZuUnterstuetztenAktionen = new HashSet<>(getSupportedImportOptions(context));

    // Die aktuellen ImportOptions müssen auch zur Auswahl stehen.
    importOptionsZuUnterstuetztenAktionen.add(getImportOptions());

    List<ImportOptions> result = new ArrayList<>(asList(ImportOptions.values()));
    result.retainAll(importOptionsZuUnterstuetztenAktionen);

    // ELECT-11892 unsinnige Option aus Auswahl entfernen
    if (!ImportOptions.NEUE_DATEN_ANLEGEN_BESTEHENDE_DATEN_LOESCHEN.equals(getImportOptions())) {
      result.remove(ImportOptions.NEUE_DATEN_ANLEGEN_BESTEHENDE_DATEN_LOESCHEN);
    }

    return result;
  }

  private Collection<ImportOptions> getSupportedImportOptions(Context context) {
    return getImporter().getSupportedImportOptions(context);
  }

  public boolean isPieChartModelRendered() {
    return !getPieChartModel().getData().isEmpty();
  }

  public PieChartModel getPieChartModel() {
    if (pieChartModel == null) {
      pieChartModel = createPieChartModel();
    }

    return pieChartModel;
  }

  private PieChartModel createPieChartModel() {
    PieChartModelBuilder pieChartModelBuilder = PieChartModelBuilder.getDefaultInstance();
    pieChartModelBuilder.setShowDataLabels(false);

    boolean loescheBestehendenDatenWennNichtInImportDatenVorhanden = getImportOptions().isLoescheBestehendenDatenWennNichtInImportDatenVorhanden();

    if (loescheBestehendenDatenWennNichtInImportDatenVorhanden) {
      addPie(pieChartModelBuilder, getImportReport().getNurInDatenbank(), loescheBestehendenDatenWennNichtInImportDatenVorhanden, "datei.import.bericht.vorab.nur.in.db.", PieChartModelBuilder.ROT);
    } else {
      addPie(pieChartModelBuilder, getImportReport().getNurInDatenbank(), loescheBestehendenDatenWennNichtInImportDatenVorhanden, "datei.import.bericht.vorab.nur.in.db.", PieChartModelBuilder.ORANGE);
    }

    addPie(pieChartModelBuilder, getImportReport().getInDateiUndInDatenbank(), getImportOptions().isAktualisiereBestehendenDaten(), "datei.import.bericht.vorab.in.datei.und.in.db.",
        PieChartModelBuilder.GELB);
    addPie(pieChartModelBuilder, getImportReport().getNurInDatei(), getImportOptions().isImportDatenUebernehmenWennNichtBereitsVorhanden(), "datei.import.bericht.vorab.nur.in.datei.",
        PieChartModelBuilder.BLAU); // Bei Grün denkt jeder, der Import war erfolgreich...

    return pieChartModelBuilder.buildModel();
  }

  protected String getMehrzahlMessage() {
    return getMetaForEntity().getMehrzahlMessage();
  }

  protected String getEinzahlMessage() {
    return getMetaForEntity().getEinzahlMessage();
  }

  private void addPie(PieChartModelBuilder pieChartModelBuilder, int anzahl, boolean option, String messageKey, String color) {
    if (anzahl > 0) {
      if (anzahl > 1) {
        pieChartModelBuilder.add(getMessage(messageKey + option, anzahl, getMehrzahlMessage()), anzahl, color);
      } else {
        pieChartModelBuilder.add(getMessage(messageKey + option + ".einzahl", anzahl, getEinzahlMessage()), anzahl, color);
      }
    }
  }

  public boolean isBtnAlleViolationsAnzeigenRendered() {
    return getImportReportViolations().size() > DEFAULT_SHOWN_VIOLATIONS;
  }

  public List<String> getBericht() {
    return asList(getImportReport().getBericht().toString().split("\n"));
  }

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

  public String getFileFormatLokalisiert() {
    if (isEntitySelected()) {
      Datei datei = getSelectedEntity().getDatei();

      if (datei != null) {
        FileFormat fileFormat = datei.getFileFormat();

        List<FileFormat> supportedFileFormats = getSupportedFileFormats(fileFormat);

        if (supportedFileFormats.size() == 1) {
          return fileFormatFormatter.format(supportedFileFormats.get(0));
        }

        return fileFormatFormatter.format(fileFormat);
      }
    }

    return null;
  }

  public boolean isSingleImportOptionsAuswahl() {
    return importViewConfig.isSingleImportOptionsAuswahl() && getSupportedImportOptions(getContext()).size() == 1;
  }

  public boolean isMultipleImportOptionsAuswahl() {
    return importViewConfig.isMultipleImportOptionsAuswahl() && !isSingleImportOptionsAuswahl();
  }

  public boolean isShowVorschau() {
    return isShowDatenbank() || isShowDatei() || isShowViolations() || isShowImportBericht();
  }

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

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

  public boolean isShowViolations() {
    return !getViolations().isEmpty();
  }

  public boolean isShowImportBericht() {
    return importViewConfig.isShowImportBericht() && getImportReport().getBericht().length() > 0;
  }

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

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