// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.security.tls;

import com.yahoo.security.tls.json.TransportSecurityOptionsJsonSerializer;
import com.yahoo.security.tls.policy.AuthorizedPeers;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;

/**
 * Generic TLS configuration for Vespa
 *
 * @author bjorncs
 */
public class TransportSecurityOptions {

    private final Path privateKeyFile;
    private final Path certificatesFile;
    private final Path caCertificatesFile;
    private final AuthorizedPeers authorizedPeers;

    private TransportSecurityOptions(Builder builder) {
        this.privateKeyFile = builder.privateKeyFile;
        this.certificatesFile = builder.certificatesFile;
        this.caCertificatesFile = builder.caCertificatesFile;
        this.authorizedPeers = builder.authorizedPeers;
    }

    public Optional<Path> getPrivateKeyFile() {
        return Optional.of(privateKeyFile);
    }

    public Optional<Path> getCertificatesFile() {
        return Optional.of(certificatesFile);
    }

    public Optional<Path> getCaCertificatesFile() {
        return Optional.of(caCertificatesFile);
    }

    public Optional<AuthorizedPeers> getAuthorizedPeers() {
        return Optional.of(authorizedPeers);
    }

    public static TransportSecurityOptions fromJsonFile(Path file) {
        try (InputStream in = Files.newInputStream(file)) {
            return new TransportSecurityOptionsJsonSerializer().deserialize(in);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static TransportSecurityOptions fromJson(String json) {
        return new TransportSecurityOptionsJsonSerializer()
                .deserialize(new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)));
    }

    public String toJson() {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        new TransportSecurityOptionsJsonSerializer().serialize(out, this);
        return new String(out.toByteArray(), StandardCharsets.UTF_8);
    }

    public void toJsonFile(Path file) {
        try (OutputStream out = Files.newOutputStream(file)) {
            new TransportSecurityOptionsJsonSerializer().serialize(out, this);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static class Builder {
        private Path privateKeyFile;
        private Path certificatesFile;
        private Path caCertificatesFile;
        private AuthorizedPeers authorizedPeers;

        public Builder() {}

        public Builder withCertificates(Path certificatesFile, Path privateKeyFile) {
            this.certificatesFile = certificatesFile;
            this.privateKeyFile = privateKeyFile;
            return this;
        }

        public Builder withCaCertificates(Path caCertificatesFile) {
            this.caCertificatesFile = caCertificatesFile;
            return this;
        }

        public Builder withAuthorizedPeers(AuthorizedPeers authorizedPeers) {
            this.authorizedPeers = authorizedPeers;
            return this;
        }

        public TransportSecurityOptions build() {
            return new TransportSecurityOptions(this);
        }
    }

    @Override
    public String toString() {
        return "TransportSecurityOptions{" +
                "privateKeyFile=" + privateKeyFile +
                ", certificatesFile=" + certificatesFile +
                ", caCertificatesFile=" + caCertificatesFile +
                ", authorizedPeers=" + authorizedPeers +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TransportSecurityOptions that = (TransportSecurityOptions) o;
        return Objects.equals(privateKeyFile, that.privateKeyFile) &&
                Objects.equals(certificatesFile, that.certificatesFile) &&
                Objects.equals(caCertificatesFile, that.caCertificatesFile) &&
                Objects.equals(authorizedPeers, that.authorizedPeers);
    }

    @Override
    public int hashCode() {
        return Objects.hash(privateKeyFile, certificatesFile, caCertificatesFile, authorizedPeers);
    }
}
@@ -1275,16 +1275,24 @@
 ional.of
+Nullable
 (private
@@ -1297,24 +1297,24 @@
 teKeyFile);%0A
-
     %7D%0A%0A    p
@@ -1384,16 +1384,24 @@
 ional.of
+Nullable
 (certifi
@@ -1497,16 +1497,24 @@
 ional.of
+Nullable
 (caCerti
@@ -1590,24 +1590,24 @@
 edPeers() %7B%0A
-
         retu
@@ -1620,16 +1620,24 @@
 ional.of
+Nullable
 (authori

Fix NPE bug - use Optional.ofNullable