
package de.ivu.elect.presentation.security;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.annotation.Nullable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.lang3.StringUtils;
import org.primefaces.model.DefaultStreamedContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.security.boundary.EmlSignKeyService;
import de.ivu.elect.business.wahl.boundary.WahlFactory;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.entity.EntityUtil;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.faces.PreConditionCheck;
import de.ivu.elect.jee.faces.UserLocale;
import de.ivu.elect.jee.faces.download.DownloadAction;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.MessageProperty;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyBooleanProperty;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyStringProperty;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.nl.x509.NlX509;
import de.ivu.elect.nl.x509.NlX509Metadata;
import de.ivu.elect.presentation.AbstractElectEditView;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.security.menu.NlPublicKeyEditMenuItem;
import de.ivu.elect.util.FileUtil;

@RequestScoped
@Named(NlPublicKeyEditView.COMPONENT_NAME)
@View(viewId = NlPublicKeyEditView.VIEW_ID, useCase = UseCasePermission.CUSTOMER_1)
public class NlPublicKeyEditView extends AbstractElectEditView<Wahl> {

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

  public static final String COMPONENT_NAME = "nlPublicKeyEditView";

  public static final String VIEW_ID = "/nl-public-key-edit.xhtml";

  private static final long serialVersionUID = 1L;

  @Inject
  private transient WahlFactory wahlFactory;

  @Inject
  private transient EmlSignKeyService emlSignKeyService;

  @Inject
  private transient NlPublicKeyEditMenuItem nlPublicKeyEditMenuItem;

  @Inject
  private transient DownloadAction downloadAction;

  @Inject
  private transient Messages messages;

  @Inject
  private transient UserLocale userLocale;

  private String password;

  public NlPublicKeyEditView() {
    super();
  }

  @Nullable
  @PreConditionCheck
  public String checkPreCondition() {
    if (nlPublicKeyEditMenuItem.isPossible()) {
      return null;
    }

    return getStartViewId(); // wechseln des privaten Schlüssels soll nicht möglich sein
  }

  @Override
  protected Wahl getEntity() {
    return wahlFactory.create();
  }

  @Override
  protected void addProperties(FormBuilder formBuilder, Wahl doNotUse) {
    NlX509Metadata nlX509Metadata = emlSignKeyService.getNlX509Metadata(getSelectedWahl());

    formBuilder.addProperty(new MessageProperty(getMessage("eml.certificate.public.key.export.help")));

    addPublicKeyInfo(formBuilder, nlX509Metadata, messages, userLocale);

    formBuilder.addSeparator("eml.certificate.private.key.separator");

    File keystore = new File("<XXX>");

    try {
      keystore = emlSignKeyService.getKeyStoreFile(getSelectedWahl());
    } catch (Exception e) {
      // ignore;
    }

    formBuilder.addProperty(new ReadOnlyStringProperty(messages.getMessageForMessageKey("eml.certificate.label.private.key.file.path"), keystore.getAbsolutePath()));
    formBuilder.addProperty(new ReadOnlyBooleanProperty(messages.getMessageForMessageKey("eml.certificate.label.private.key.file.present"), keystore.isFile()));
    formBuilder.addProperty(new ReadOnlyBooleanProperty(messages.getMessageForMessageKey("eml.certificate.label.private.key.valid"), isPrivateKeyValid()));

  }

  public static void addPublicKeyInfo(FormBuilder formBuilder, NlX509Metadata nlX509Metadata, Messages messages, UserLocale userLocale) {
    formBuilder.addSeparator("eml.certificate.public.key.separator");
    EmlSignKeyService.collectNlX509MetadataFormatted(messages, userLocale, nlX509Metadata, "").forEach((label, value) -> formBuilder.addProperty(new ReadOnlyStringProperty(label, value)));
  }

  private Boolean isPrivateKeyValid;

  private boolean isPrivateKeyValid() {
    if (isPrivateKeyValid == null) {
      isPrivateKeyValid = emlSignKeyService.isPrivateKeyValid(getSelectedWahl());
    }

    return isPrivateKeyValid;
  }

  @Override
  @Nullable
  public String delete() {
    return null;
  }

  @Override
  public boolean isBtnSaveRendered() {
    return false;
  }

  public boolean isBtnAbrufRendered() {
    return getSelectedWahl() != null && isPrivateKeyValid();
  }

  @Override
  protected boolean isSavePermissionVorhanden() {
    return true;
  }

  @Override
  @Redirect
  public String save() {
    PrivateKey privateKey = emlSignKeyService.loadPrivateKey(getSelectedWahl());
    PublicKey publicKey = emlSignKeyService.loadPublicKey(getSelectedWahl());
    NlX509Metadata nlX509Metadata = emlSignKeyService.getNlX509Metadata(getSelectedWahl());

    try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
      new NlX509().exportPublicKeyAsX509(bos, privateKey, publicKey, nlX509Metadata);
      byte[] content = bos.toByteArray();

      String type = normalisiereTyp(nlX509Metadata.getOrganisationTypeAndRegionName());
      String filename = messages.getMessageForMessageKey("eml.x509.filename", nlX509Metadata.getOrganisationName(), type).toLowerCase();
      emlSignKeyService.downloadPublicKey(getSelectedWahl());
      DefaultStreamedContent.Builder streamedContentBuilder = DefaultStreamedContent.builder()
          .stream(() -> new ByteArrayInputStream(content))
          .contentType("application/x-x509-ca-cert")
          .name(filename)
          .contentEncoding(StandardCharsets.UTF_8.name())
          .contentLength(content.length);
      downloadAction.download(streamedContentBuilder.build());
    } catch (Exception e) {
      LOG.warn("Fehler: " + e.getMessage(), e);
      addErrorMessage("exception.unknown.message");
    } finally {
      try {
        privateKey.destroy();
      } catch (Exception e) {
        // ignore
      }
    }
    return VIEW_ID;
  }

  private String normalisiereTyp(String organisationTypeAndRegionName) {
    if (StringUtils.isBlank(organisationTypeAndRegionName)) {
      return "";
    }

    String trimmed = organisationTypeAndRegionName.trim();

    if (trimmed.contains(" ")) {
      String type = trimmed.substring(0, trimmed.indexOf(" "));
      String rest = trimmed.substring(trimmed.indexOf(" ") + 1);
      return type + "-" + (FileUtil.normalizeFileName(EntityUtil.normalizeCaseAndDiacritics(rest)).replace("_", "").replace(" ", ""));
    }

    return organisationTypeAndRegionName;
  }

  @Override
  public String getBtnSaveLabel() {
    return getMessage("eml.public.key.abrufen");
  }

  @Nullable
  @Override
  @Redirect
  public String reset() {
    return null;
  }

  @Override
  protected boolean isGebietBearbeitenRechtPruefen() {
    return false;
  }

  @Override
  public boolean isBtnResetRendered() {
    return false;
  }

  @Override
  @Redirect
  public String cancel() {
    return getStartViewId();
  }


  @Override
  public boolean isBtnCancelRendered() {
    return false; // ELECT-11693
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }
}
