
package de.ivu.elect.presentation.wahl.exchange;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

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

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.exchange.control.AbstractExporter;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.control.GebietByBreadthFirstComparator;
import de.ivu.elect.business.gebietsbaum.entity.AbstractGebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietPK;
import de.ivu.elect.business.gebietsbaum.entity.GebietTK;
import de.ivu.elect.business.konfiguration.entity.ElectKonfigurationWerte;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.stimmzettel.boundary.StimmzettelErfassungSupport;
import de.ivu.elect.business.wahl.boundary.WahlExtendedDAO;
import de.ivu.elect.business.wahl.boundary.WahlFuerWesExporter;
import de.ivu.elect.business.wahl.boundary.WesTemplateFinder;
import de.ivu.elect.business.wahl.control.WahlExportObject;
import de.ivu.elect.business.wahl.control.WahlWesExportContext;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.business.wahl.entity.WesUebertragungsweg;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaBooleanProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaEnumProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaLongProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaStringProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaStringSelectOneProperty;
import de.ivu.elect.jee.presentation.dynamicform.Property;
import de.ivu.elect.jee.presentation.dynamicform.SeparatorPseudoProperty;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.exchange.AbstractExportSearchView;
import de.ivu.elect.presentation.exchange.ExportDescription;
import de.ivu.elect.presentation.gebietsbaum.GebietNummerFormatter;
import de.ivu.elect.presentation.gebietsbaum.form.GebietePicklistProperty;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;
import de.ivu.elect.presentation.security.EinsehenGebietSecurity;
import de.ivu.elect.presentation.system.BasisUrlResolver;


import static java.util.Arrays.asList;

@OnlineHilfeId("798")
@Named(WahlFuerWesExportView.COMPONENT_NAME)
@View(viewId = WahlFuerWesExportView.VIEW_ID, useCase = UseCasePermission.WAHL_FUER_WES_EXPORTIEREN)
public class WahlFuerWesExportView extends AbstractExportSearchView<List<WahlExportObject>, WahlWesExportContext> {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "wahlFuerWesExportView";

  public static final String VIEW_ID = "/wahl/wahl-fuer-wes-export.xhtml";

  @Inject
  private transient WahlFuerWesExporter wahlFuerWesExporter;

  @Inject
  private transient WahlFuerWesExportDescription wahlFuerWesExportDescription;

  @Inject
  private transient StimmzettelErfassungSupport stimmzettelErfassungSupport;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Inject
  private transient GebietNummerFormatter gebietNummerFormatter;

  @Inject
  private transient WahlExtendedDAO wahlExtendedDAO;


  @Inject
  private transient WahlFuerWesExportScope wahlFuerWesExportScope;

  @Inject
  private transient EinsehenGebietSecurity einsehenGebietSecurity;

  @Inject
  private transient WesTemplateFinder wesTemplateFinder;

  @Inject
  private transient BasisUrlResolver basisUrlResolver;

  private Set<WahlPK> weitereWahlenZuExportierren = new HashSet<>();

  private Set<String> zugeordneteGebiete = new HashSet<>();

  private String wesTemplateFile;

  private WesUebertragungsweg wesUebertragungsweg;

  private String wasUrl;

  private Long restSocketTimeoutInSekunden = 60L;

  private Long restConnectTimeoutInSekunden = 60L;

  private boolean restEnabledTrustmanager = false;

  private String restProxyHost = null;

  private Long restProxyPort = null;

  private boolean wahlIdMitZeitstempel = true;

  public WahlFuerWesExportView() {
    super();
  }

  @Override
  protected AbstractExporter<List<WahlExportObject>, WahlWesExportContext> getExporter() {
    return wahlFuerWesExporter;
  }

  @PostConstruct
  public void init() {
    wasUrl = basisUrlResolver.bestimmeBasisUrl();
  }

  @Override
  protected WahlWesExportContext getContext() {
    List<Wahl> weitereWahlen = Collections.emptyList();

    if (weitereWahlenZuExportierren != null && !weitereWahlenZuExportierren.isEmpty()) {
      weitereWahlen = weitereWahlenZuExportierren.stream().map(wahlPK -> wahlExtendedDAO.findByPrimaryKey(wahlPK)).collect(Collectors.toList());
    }

    Set<GebietTK> gebiete = Collections.emptySet();
    if (zugeordneteGebiete != null && !zugeordneteGebiete.isEmpty()) {
      gebiete = zugeordneteGebiete.stream().map(id -> gebietExtendedDAO.findByPrimaryKey(new GebietPK(Long.valueOf(id))).getGebietTK())
          .collect(Collectors.toSet());
    }

    return new WahlWesExportContext(getSelectedWahl(), weitereWahlen, gebiete, wesTemplateFile, wahlFuerWesExportScope.getUsbPath(), wesUebertragungsweg,
        wasUrl, restSocketTimeoutInSekunden, restConnectTimeoutInSekunden, restEnabledTrustmanager, restProxyHost, restProxyPort, wahlIdMitZeitstempel);
  }

  @Override
  protected void preSelectFileFormat(List<FileFormat> supportedFileFormats) {
    if (getSelectedFileFormat() == null) {
      for (FileFormat supportedFileFormat : supportedFileFormats) {
        if (FileType.EXE.equals(supportedFileFormat.getFileType())) {
          setSelectedFileFormat(supportedFileFormat);
          return;
        }
      }
    }
    super.preSelectFileFormat(supportedFileFormats);
  }

  @Override
  public ExportDescription getExportDescription() {
    return wahlFuerWesExportDescription;
  }

  @Override
  protected void addProperties(FormBuilder formBuilder, @Nullable FileFormat fileFormat, WahlWesExportContext context) {
    super.addProperties(formBuilder, fileFormat, context);

    // ELECT-9131 Deaktiviert weil daurch verwirrt wird
//    formBuilder.addProperty(new LambdaStringProperty(getMessage("wahl.fuer.wes.export.exe.install.path"),
//        false, s -> {
//      wahlFuerWesExportScope.setUsbPath(s);
//    }, () -> wahlFuerWesExportScope.getUsbPath(), 0, 255
//    ));

    Wahl selectedWahl = getSelectedWahl();
    if (selectedWahl != null) {

      List<String> wesTempates = wesTemplateFinder.getTemplateFiles();
      if (wesTempates.isEmpty()) {
        addWarningMessage("wahl.fuer.wes.export.keine.wes.templates.vorhanden", getMessage(ElectKonfigurationWerte.WES_TEMPLATE_ABLAGEPFAD));
      } else {
        this.wesTemplateFile = wesTempates.get(0);
      }

      formBuilder.addProperty(new LambdaStringSelectOneProperty(getMessage("wahl.fuer.wes.export.dokumentvorlage"), false,
          this::setWesTemplateFile, this::getWesTemplateFile, wesTempates, true));

      formBuilder.addSeparator("wahl.fuer.wes.export.uebertragungsweg.separator");
      LambdaEnumProperty<WesUebertragungsweg> wesUebertragungswegLambdaEnumProperty = new LambdaEnumProperty<>(
          getMessage("wahl.fuer.wes.export.uebertragungsweg"), true, this::setWesUebertragungsweg, this::getWesUebertragungsweg, WesUebertragungsweg.class,
          asList(WesUebertragungsweg.values()), true);
      formBuilder.addProperty(wesUebertragungswegLambdaEnumProperty);


      addUebertragungswegProperty(formBuilder, new SeparatorPseudoProperty("wahl.fuer.wes.export.online.separator"), wesUebertragungswegLambdaEnumProperty);

      addUebertragungswegProperty(formBuilder, new LambdaStringProperty(getMessage("wahl.fuer.wes.export.was.url"),
          false, this::setWasUrl, this::getWasUrl, 0, 1024), wesUebertragungswegLambdaEnumProperty);

      addUebertragungswegProperty(formBuilder, new LambdaLongProperty(getMessage("wahl.fuer.wes.export.socket.timeout"),
          true, this::setRestSocketTimeoutInSekunden, this::getRestSocketTimeoutInSekunden), wesUebertragungswegLambdaEnumProperty);

      addUebertragungswegProperty(formBuilder, new LambdaLongProperty(getMessage("wahl.fuer.wes.export.connect.timeout"),
          true, this::setRestConnectTimeoutInSekunden, this::getRestConnectTimeoutInSekunden), wesUebertragungswegLambdaEnumProperty);

      addUebertragungswegProperty(formBuilder, new LambdaBooleanProperty(getMessage("wahl.fuer.wes.export.enable.trustmanager"),
          true, this::setRestEnabledTrustmanager, this::isRestEnabledTrustmanager), wesUebertragungswegLambdaEnumProperty);

      addUebertragungswegProperty(formBuilder, new LambdaStringProperty(getMessage("wahl.fuer.wes.export.proxy.host"),
          false, this::setRestProxyHost, this::getRestProxyHost, 0, 255), wesUebertragungswegLambdaEnumProperty);

      addUebertragungswegProperty(formBuilder, new LambdaLongProperty(getMessage("wahl.fuer.wes.export.proxy.port"),
          false, this::setRestProxyPort, this::getRestProxyPort), wesUebertragungswegLambdaEnumProperty);

      formBuilder.addSeparator("wahl.fuer.wes.export.wahlen");

      List<Wahl> weitereWahlen = selectedWahl.getWahltermin().getWahlenSortiertNachPosition().stream()
          .filter(w -> !w.equals(selectedWahl))
          .filter(w -> stimmzettelErfassungSupport.isStimmzettelErfassungVerfuegbar(w))
          .filter(w -> w.isRechtZumEinsehenVorhanden(getPrincipal()))
          .collect(Collectors.toList());

      formBuilder.addProperty(new LambdaBooleanProperty(getMessage("wahl.fuer.wes.export.weitere.wahl", selectedWahl.getName()), false, b -> {
      }, () -> true)).withReadonly(true);

      for (Wahl wahl : weitereWahlen) {
        formBuilder.addProperty(new LambdaBooleanProperty(getMessage("wahl.fuer.wes.export.weitere.wahl", wahl.getName()), false, b -> {
          if (b) {
            weitereWahlenZuExportierren.add(wahl.getPk());
          } else {
            weitereWahlenZuExportierren.remove(wahl.getPk());
          }
        }, () -> weitereWahlenZuExportierren.contains(wahl.getPk())));
      }

      formBuilder.addProperty(new LambdaBooleanProperty(getMessage("wahl.fuer.wes.export.wahlid.zeitstempel"), true,false, this::setWahlIdMitZeitstempel,
          this::isWahlIdMitZeitstempel));


      formBuilder.addSeparator("wahl.fuer.wes.export.gebiete");
      List<Gebiet> alleGebiete = gebietExtendedDAO.findByWahl(selectedWahl).stream().filter(AbstractGebiet::isErfassungsgebiet)
          .filter(g -> einsehenGebietSecurity.isRechtVorhanden(g))
          .sorted(GebietByBreadthFirstComparator.INSTANCE).collect(Collectors.toList());

      Function<Gebiet, String> gebietNummerFormatterExtended = g -> gebietNummerFormatter.getDarstellung(gebietExtendedDAO.attach(g));

      GebietePicklistProperty<Gebiet> gebietePicklistProperty = new GebietePicklistProperty<>(alleGebiete, g -> g.getId().toString(), this.zugeordneteGebiete,
          getMessage("wahl.fuer.wes.export.gebiet.auswahl"), "wahl.fuer.wes.export.gebiet.nicht.ausgewaehlt",
          "wahl.fuer.wes.export.gebiet.ausgewaehlt", gebietNummerFormatterExtended, s -> this.zugeordneteGebiete = s, true);

      formBuilder.addProperty(gebietePicklistProperty);
    }
  }

  private void addUebertragungswegProperty(FormBuilder formBuilder, Property property,
                                           LambdaEnumProperty<WesUebertragungsweg> wesUebertragungswegLambdaEnumProperty) {

    boolean initialRequired = property.isRequired();
    formBuilder.addProperty(property).withObservedProperty(wesUebertragungswegLambdaEnumProperty);
    property.appendObservedPropertyValueChangedConsumer(c -> {
      if (c.equals(wesUebertragungswegLambdaEnumProperty)) {
        property.setVisible(wesUebertragungswegLambdaEnumProperty.getValue() != null && wesUebertragungswegLambdaEnumProperty.getValue().isOnline());
        if (property.isVisible()) {
          property.setRequired(initialRequired);
        } else {
          property.setRequired(false);
        }
      }
    });
    property.setVisible(wesUebertragungswegLambdaEnumProperty.getValue() != null && wesUebertragungswegLambdaEnumProperty.getValue().isOnline());
    if (property.isVisible()) {
      property.setRequired(initialRequired);
    } else {
      property.setRequired(false);
    }
  }

  public String getWesTemplateFile() {
    return wesTemplateFile;
  }

  public void setWesTemplateFile(String wesTemplateFile) {
    this.wesTemplateFile = wesTemplateFile;
  }

  public WesUebertragungsweg getWesUebertragungsweg() {
    return wesUebertragungsweg;
  }

  public void setWesUebertragungsweg(WesUebertragungsweg wesUebertragungsweg) {
    this.wesUebertragungsweg = wesUebertragungsweg;
  }

  public String getWasUrl() {
    return wasUrl;
  }

  public void setWasUrl(String wasUrl) {
    this.wasUrl = wasUrl;
  }

  @Override
  @Redirect
  public String exportieren() {
    if (wesUebertragungsweg!=null) {
      String msgKey = wesUebertragungsweg.validate(wasUrl);
      if (StringUtils.isNotBlank(msgKey)) {
        addErrorMessage(msgKey);
        return FacesContextUtil.getCurrentViewId();
      }
    }
    return super.exportieren();
  }

  public Long getRestSocketTimeoutInSekunden() {
    return restSocketTimeoutInSekunden;
  }

  public void setRestSocketTimeoutInSekunden(Long restSocketTimeoutInSekunden) {
    this.restSocketTimeoutInSekunden = restSocketTimeoutInSekunden;
  }

  public Long getRestConnectTimeoutInSekunden() {
    return restConnectTimeoutInSekunden;
  }

  public void setRestConnectTimeoutInSekunden(Long restConnectTimeoutInSekunden) {
    this.restConnectTimeoutInSekunden = restConnectTimeoutInSekunden;
  }

  public boolean isRestEnabledTrustmanager() {
    return restEnabledTrustmanager;
  }

  public void setRestEnabledTrustmanager(boolean restEnabledTrustmanager) {
    this.restEnabledTrustmanager = restEnabledTrustmanager;
  }

  public String getRestProxyHost() {
    return restProxyHost;
  }

  public void setRestProxyHost(String restProxyHost) {
    this.restProxyHost = restProxyHost;
  }

  public Long getRestProxyPort() {
    return restProxyPort;
  }

  public void setRestProxyPort(Long restProxyPort) {
    this.restProxyPort = restProxyPort;
  }

  public boolean isWahlIdMitZeitstempel() {
    return wahlIdMitZeitstempel;
  }

  public void setWahlIdMitZeitstempel(boolean wahlIdMitZeitstempel) {
    this.wahlIdMitZeitstempel = wahlIdMitZeitstempel;
  }
}
