// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.feed.client;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;

/**
 * Main method for CLI interface
 *
 * @author bjorncs
 */
public class CliClient {

    private final PrintStream systemOut;
    private final PrintStream systemError;
    private final InputStream systemIn;
    private final Properties systemProperties;

    private CliClient(PrintStream systemOut, PrintStream systemError, InputStream systemIn, Properties systemProperties) {
        this.systemOut = systemOut;
        this.systemError = systemError;
        this.systemIn = systemIn;
        this.systemProperties = systemProperties;
    }

    public static void main(String[] args) {
        CliClient client = new CliClient(System.out, System.err, System.in, System.getProperties());
        int exitCode = client.run(args);
        System.exit(exitCode);
    }

    private int run(String[] rawArgs) {
        CliArguments cliArgs = null;
        try {
            cliArgs = CliArguments.fromRawArgs(rawArgs);
            if (cliArgs.helpSpecified()) {
                cliArgs.printHelp(systemOut);
                return 0;
            }
            if (cliArgs.versionSpecified()) {
                systemOut.println(Vespa.VERSION);
                return 0;
            }
            try (InputStream in = createFeedInputStream(cliArgs); JsonStreamFeeder feeder = createJsonFeeder(cliArgs)) {
                feeder.feed(in);
            }
            return 0;
        } catch (CliArguments.CliArgumentsException | IOException e) {
            boolean verbose = cliArgs != null && cliArgs.verboseSpecified();
            return handleException(verbose, e);
        }
    }

    private static FeedClient createFeedClient(CliArguments cliArgs) throws CliArguments.CliArgumentsException, IOException {
        FeedClientBuilder builder = FeedClientBuilder.create(cliArgs.endpoint());
        cliArgs.connections().ifPresent(builder::setMaxConnections);
        cliArgs.maxStreamsPerConnection().ifPresent(builder::setMaxConnections);
        if (cliArgs.sslHostnameVerificationDisabled()) {
            builder.setHostnameVerifier(AcceptAllHostnameVerifier.INSTANCE);
        }
        CliArguments.CertificateAndKey certificateAndKey = cliArgs.certificateAndKey().orElse(null);
        Path caCertificates = cliArgs.caCertificates().orElse(null);
        if (certificateAndKey != null || caCertificates != null) {
            SslContextBuilder sslContextBuilder = new SslContextBuilder();
            if (certificateAndKey != null) {
                sslContextBuilder.withCertificateAndKey(certificateAndKey.certificateFile, certificateAndKey.privateKeyFile);
            }
            if (caCertificates != null) {
                sslContextBuilder.withCaCertificates(caCertificates);
            }
            builder.setSslContext(sslContextBuilder.build());
        }
        cliArgs.headers().forEach(builder::addRequestHeader);
        return builder.build();
    }

    private static JsonStreamFeeder createJsonFeeder(CliArguments cliArgs) throws CliArguments.CliArgumentsException, IOException {
        FeedClient feedClient = createFeedClient(cliArgs);
        JsonStreamFeeder.Builder builder = JsonStreamFeeder.builder(feedClient);
        cliArgs.timeout().ifPresent(builder::withTimeout);
        cliArgs.route().ifPresent(builder::withRoute);
        cliArgs.traceLevel().ifPresent(builder::withTracelevel);
        return builder.build();
    }

    private InputStream createFeedInputStream(CliArguments cliArgs) throws CliArguments.CliArgumentsException, IOException {
        return new BufferedInputStream(
                cliArgs.readFeedFromStandardInput() ? systemIn : Files.newInputStream(cliArgs.inputFile().get()));
    }

    private int handleException(boolean verbose, Exception e) { return handleException(verbose, e.getMessage(), e); }

    private int handleException(boolean verbose, String message, Exception exception) {
        systemError.println(message);
        if (debugMode() || verbose) {
            exception.printStackTrace(systemError);
        }
        return 1;
    }

    private boolean debugMode() {
        return Boolean.parseBoolean(systemProperties.getProperty("VESPA_DEBUG", Boolean.FALSE.toString()));
    }

    private static class AcceptAllHostnameVerifier implements HostnameVerifier {
        static final AcceptAllHostnameVerifier INSTANCE = new AcceptAllHostnameVerifier();
        @Override public boolean verify(String hostname, SSLSession session) { return true; }
    }
}

@@ -361,35 +361,8 @@
 es;%0A
-import java.nio.file.Path;%0A
 impo
@@ -2493,395 +2493,62 @@
     
-CliArguments.CertificateAndKey certificateAndKey = cliArgs.certificateAndKey().orElse(null);%0A        Path caCertificates = cliArgs.caCertificates().orElse(null);%0A        if (certificateAndKey != null %7C%7C caCertificates != null) %7B%0A            SslContextBuilder sslContextBuilder = new SslContextBuilder();%0A            if (certificateAndKey != null) %7B%0A                sslContextB
+cliArgs.certificateAndKey().ifPresent(c -%3E b
 uilder.
-with
+set
 Cert
@@ -2558,32 +2558,10 @@
 cate
-AndKey(certificateAndKey
+(c
 .cer
@@ -2575,32 +2575,16 @@
 eFile, c
-ertificateAndKey
 .private
@@ -2591,16 +2591,17 @@
 KeyFile)
+)
 ;%0A      
@@ -2606,94 +2606,55 @@
     
-    %7D%0A            if (caCertificates != null) %7B%0A                sslContextB
+cliArgs.caCertificates().ifPresent(b
 uilder
-.with
+::set
 CaCe
@@ -2667,111 +2667,10 @@
 ates
-(caCertificates);%0A            %7D%0A            builder.setSslContext(sslContextBuilder.build());%0A        %7D
+);
 %0A   

Simplify using new methods on client builder