
package de.ivu.elect;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.annotation.Nullable;
import javax.security.auth.DestroyFailedException;

import de.ivu.elect.business.eml.control.EmlHashCode;
import de.ivu.elect.business.exchange.control.AbstractExchangeContext;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietTK;
import de.ivu.elect.nl.eml.EmlWrapper;
import de.ivu.elect.nl.eml.EmlWriter;
import de.ivu.elect.nl.x509.NlCMS;
import de.ivu.elect.nl.x509.NlX509Metadata;

public class EmlExportMapperHelper implements Serializable {

  private static final long serialVersionUID = 1L;

  private static final int BUFFER_SIZE = 64 * 1024;

  private static final boolean INCLUDE_HASH = false; // ELECTVAPP-794

  public EmlExportMapperHelper() {
    super();
  }

  public void writeEml(EmlWrapper eml, ZipOutputStream zos, String fileName, @Nullable Gebiet gebiet, AbstractExchangeContext context, @Nullable PrivateKey privateKey, @Nullable PublicKey publicKey,
      @Nullable NlX509Metadata nlX509Metadata) throws IOException {
    writeEml(eml, zos, fileName, gebiet, context, privateKey, publicKey, nlX509Metadata, INCLUDE_HASH);
  }

  public void writeEml(EmlWrapper eml, ZipOutputStream zos, String fileName, @Nullable Gebiet gebiet, AbstractExchangeContext context, @Nullable PrivateKey privateKey, @Nullable PublicKey publicKey,
      @Nullable NlX509Metadata nlX509Metadata, boolean includeHashFile) throws IOException {
    zos.putNextEntry(new ZipEntry(fileName));

    EmlWriter emlWriter = new EmlWriter();
    Set<String> hashCodeContainer = new HashSet<>();

    try (ByteArrayOutputStream emlContent = new ByteArrayOutputStream(BUFFER_SIZE)) {
      emlWriter.writeEMlHash(eml, emlContent, hashCodeContainer::add);

      emlContent.writeTo(zos);

      zos.closeEntry();

      /*
       * Bei Anpassungen an den Hash-Code-Context-Parametern auch AbstractNlExportArtefaktNachSystemExportstatusExportContext#addHashCodesPerFileFormat anpassen!!!
       */

      String hashCode = hashCodeContainer.iterator().next();
      context.put(EmlHashCode.EML_EXPORT_LAST_HASHCODE_KEY, hashCode);

      if (gebiet != null) {
        Map<GebietTK, String> hashCodePerGebiet = context.get(EmlHashCode.EML_EXPORT_HASHCODE_KEY_PRO_GEBIET, Map.class);

        if (hashCodePerGebiet == null) {
          hashCodePerGebiet = new HashMap<>();
        }

        hashCodePerGebiet.put(gebiet.getGebietTK(), hashCode);

        context.put(EmlHashCode.EML_EXPORT_HASHCODE_KEY_PRO_GEBIET, hashCodePerGebiet);
      }

      if (includeHashFile) {
        zos.putNextEntry(new ZipEntry(fileName + EmlExportMapper.EML_HASHCODE_FILE_EXTENSION));
        zos.write(hashCode.getBytes(StandardCharsets.UTF_8));
        zos.closeEntry();
      }

      // ELECT-9292, ELECTVAPP-978
      if (privateKey != null && publicKey != null && nlX509Metadata != null) {
        try {
          zos.putNextEntry(new ZipEntry(fileName + EmlExportMapper.EML_SIGNATURE_FILE_EXTENSION));
          //  byte[] signatureData = new Sign(new NlSignConfig()).sign(emlContent.toByteArray(), privateKey);
          //  String signatureBase64 = Base64.getEncoder().encodeToString(signatureData);
          //  zos.write(signatureBase64.getBytes(StandardCharsets.UTF_8));
          new NlCMS().sign(zos, privateKey, publicKey, nlX509Metadata, emlContent.toByteArray());
          zos.closeEntry();
        } catch (Exception ex) {
          throw new RuntimeException(ex.getMessage(), ex);
        } finally {
          try {
            privateKey.destroy();
          } catch (DestroyFailedException ignored) {
          }
        }
      }
    }
  }
}
