package be.iminds.ilabt.jfed.examples;

import be.iminds.ilabt.jfed.*;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.Server;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.Service;
import be.iminds.ilabt.jfed.log.Logger;
import be.iminds.ilabt.jfed.lowlevel.*;
import be.iminds.ilabt.jfed.lowlevel.api.AbstractGeniAggregateManager;
import be.iminds.ilabt.jfed.lowlevel.api.AggregateManager3;
import be.iminds.ilabt.jfed.lowlevel.api.ProtogeniSliceAuthority;
import be.iminds.ilabt.jfed.lowlevel.authority.AuthorityFinder;

import be.iminds.ilabt.jfed.lowlevel.connection.*;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import be.iminds.ilabt.jfed.util.IOUtils;
import be.iminds.ilabt.jfed.util.JFedTrustStore;
import com.google.inject.Guice;
import com.google.inject.Injector;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class ClientApiExample {

    public static void main(String[] args) throws Exception {
        try {
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////

            Logger logger = new Logger();

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////

            File defaultPemKeyCertFile =
                    new File(System.getProperty("user.home") + File.separator + ".ssl" + File.separator + "geni_cert.pem");
            String pemKeyCertFilename =
                    IOUtils.askCommandLineInput("PEM key and certificate filename (default: \"" + defaultPemKeyCertFile.getPath() + "\")");
            char[] pass = IOUtils.askCommandLinePassword("Key password (if any)");
            if (pemKeyCertFilename == null || pemKeyCertFilename.equals(""))
                pemKeyCertFilename = defaultPemKeyCertFile.getPath();


            //////////////////////////////////////////// Setup user ////////////////////////////////////////////////////

            SimpleGeniUser user = new SimpleGeniUser(null /*DirectUserModule will fill this in*/, null, IOUtils.fileToString(pemKeyCertFilename),
                    (pass.length == 0) ? null : pass, defaultPemKeyCertFile, defaultPemKeyCertFile);

            Injector injector = Guice.createInjector(new BasicTestbedInfoModule(), new JFedWebApiClientModule(), new DirectUserModule(user), new PreLoginCoreModule(), new PostLoginCoreModule(), new CorePreferencesModule());

            GeniUser loggedInUser = injector.getInstance(GeniUser.class);
            if (loggedInUser != null) {
                System.out.println("You are logged in as user: " + loggedInUser.getUserUrn());
                if (loggedInUser.getUserAuthorityServer() != null) {
                    System.out.println("             on authority: " + loggedInUser.getUserAuthorityServer().getDefaultComponentManagerUrn());
                    if (loggedInUser.getUserAuthorityServer().getServices() == null || loggedInUser.getUserAuthorityServer().getServices().isEmpty()) {
                        System.err.println("ERROR: there are no services known for your user authority! (services="+loggedInUser.getUserAuthorityServer().getServices()+")");
                        System.exit(1);
                    }
                    if (Objects.equals(loggedInUser.getUserUrnString(), user.getUserUrnString())) {
                        System.err.println("WARNING: the user you are now logged in with, does not match the user you started login with. (this should not happen)");
                    }
                } else {
                    System.err.println("ERROR: the authority of your user was not found!");
                    System.exit(1);
                }
            } else {
                System.err.println("ERROR: null user!");
                System.exit(1);
            }

            ////////////////////////////////////////// Get target test authority ///////////////////////////////////////

            TestbedInfoSource testbedInfoSource =  injector.getInstance(TestbedInfoSource.class);
            AuthorityFinder authorityFinder =  injector.getInstance(AuthorityFinder.class);
            Server wall = authorityFinder.findByUrn("urn:publicid:IDN+wall2.ilabt.iminds.be+authority+cm", AuthorityFinder.Purpose.CREATE_SLIVER);

            ///////////////////////////////////////////// Call AMv3 GetVersion /////////////////////////////////////////

            JFedPreferences jFedPreferences = injector.getInstance(JFedPreferences.class);
            AggregateManager3 am3 = new AggregateManager3(logger, jFedPreferences);

            Service wallAm3Service = ApiInfo.findService(wall, ApiInfo.ApiName.GENI_AM, 3);
            URL wallAm3Url = new URL(wallAm3Service.getUrl());

            //manually build a connection. (A lot easier to just use SfaConnectionPool which builds the connection for you)

            ConnectionBuilder connectionBuilder = new BasicConnectionBuilderFactory().createConnectionBuilder();
            connectionBuilder.setDebugInfo(new JFedConnection.DebugInfo(wallAm3Service, user));
            connectionBuilder.setProxy(null, false);
            connectionBuilder.setUrl(wallAm3Url);
            connectionBuilder.useHttps(new JFedTrustStore(wall), null/*handleUntrustedCallback*/);
            connectionBuilder.useSslClientAuthentication(user.getClientCertificateChain(), user.getPrivateKey());
            SfaConnection am3Con = connectionBuilder.buildSfaConnection();

            AbstractGeniAggregateManager.AggregateManagerReply<AggregateManager3.VersionInfo> amGetVersionReply =
                    am3.getVersion(am3Con);
            if (!amGetVersionReply.getGeniResponseCode().isSuccess()) {
                System.err.println("GetVersion failed");
                System.exit(-1);
            }
            AggregateManager3.VersionInfo versionInfo = amGetVersionReply.getValue();
            System.out.println("\n\nServer supports single_allocation:" + versionInfo.isSingleSliceAllocation() + "\n");


            ///////////////////// Get a credential from the SA. This time use a ConnectionPool /////////////////////////

            ProtogeniSliceAuthority sa = new ProtogeniSliceAuthority(logger, jFedPreferences);

            JFedConnectionProvider conPool = injector.getInstance(JFedConnectionProvider.class);
            SfaConnection saCon =
                    (SfaConnection) conPool.getConnectionByAuthority(user, wall, new ApiInfo.Api(ApiInfo.ApiName.PROTOGENI_SA, 1));

            ProtogeniSliceAuthority.SliceAuthorityReply<AnyCredential> saReply = sa.getCredential(saCon);
            AnyCredential cred = saReply.getValue();
            System.out.println("\n\ncredential:\n" + cred.getCredentialXml().substring(0,
                    cred.getCredentialXml().length() > 300 ? 300 : cred.getCredentialXml().length()) + "...\n");

            //////////////////////////////////////////// Call AMv3 ListResources ///////////////////////////////////////

            List<AnyCredential> credList = new ArrayList<>();
            credList.add(cred);
            AbstractGeniAggregateManager.AggregateManagerReply<String> listResourcesReply = am3.listResources(
                    am3Con, credList, "geni", "3", true/*available*/, true /*compressed*/, null/*extra options*/);
            if (listResourcesReply.getGeniResponseCode().isSuccess())
                System.out.println("\n\nAdvertisement RSpec: " + listResourcesReply.getValue());
            else
                System.out.println("\n\nListResources failed: " + listResourcesReply.getGeniResponseCode() + " output=" + listResourcesReply.getOutput());

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////

            System.exit(0);
        } catch (JFedException e) {
            System.err.println("JFedException: " + e);
            e.printStackTrace();
            if (e.getXmlRpcResult() != null) {
                System.err.println("HTTP request:\n" + e.getXmlRpcResult().getRequestHttpContent());
                System.err.println("\nHTTP response:\n" + e.getXmlRpcResult().getResultHttpContent());
                System.err.println("\nXMLRPC request:\n" + e.getXmlRpcResult().getRequestXmlRpcString());
                System.err.println("\nXMLRPC response:\n" + e.getXmlRpcResult().getResultXmlRpcString());
            }
            System.exit(1);
        }
    }
}
