package be.iminds.ilabt.jfed.connectivity_tester;


import be.iminds.ilabt.jfed.log.ApiCallDetails;
import be.iminds.ilabt.jfed.lowlevel.GeniUser;
import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.lowlevel.ApiInfo;
import be.iminds.ilabt.jfed.lowlevel.TestbedInfoSource;
import be.iminds.ilabt.jfed.lowlevel.api.AbstractGeniAggregateManager;
import be.iminds.ilabt.jfed.lowlevel.api.AggregateManager2;
import be.iminds.ilabt.jfed.lowlevel.api.AggregateManager3;
import be.iminds.ilabt.jfed.lowlevel.authority.AuthorityFinder;

import be.iminds.ilabt.jfed.fedmon.webapi.service.json.*;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnection;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnectionProvider;
import be.iminds.ilabt.jfed.lowlevel.connection.SfaConnection;
import be.iminds.ilabt.jfed.lowlevel.connection.SfaConnectionPool;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import be.iminds.ilabt.jfed.util.JavaFXLogger;
import org.slf4j.LoggerFactory;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class GetVersionTest implements ConnectivityTest {
    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(GetVersionTest.class);

    private static final int TIMEOUT = 5000; //5 seconds
    private final URL url;
    private final Server auth;

    private final JFedConnection.ProxyInfo proxyInfo;

    private final String description;
    private final String category = "GetVersion";

    private final int version; //AM API version

    private final GeniUserProvider geniUserProvider;
    private final JavaFXLogger logger;
    private final JFedConnectionProvider jFedConnectionProvider;
    private final JFedPreferences jFedPreferences;


    public GetVersionTest(Server auth, GeniUserProvider geniUserProvider, JavaFXLogger logger, JFedConnectionProvider jFedConnectionProvider, JFedPreferences jFedPreferences) throws MalformedURLException {
        this(auth, null, 2, geniUserProvider, logger, jFedConnectionProvider, jFedPreferences);
    }

    public GetVersionTest(String authUrn, TestbedInfoSource testbedInfoSource, AuthorityFinder authorityFinder, GeniUserProvider geniUserProvider, JavaFXLogger logger, JFedConnectionProvider jFedConnectionProvider, JFedPreferences jFedPreferences) {
        this(authorityFinder.findByUrn(authUrn, AuthorityFinder.Purpose.CREATE_SLIVER),
                null, 2, geniUserProvider, logger, jFedConnectionProvider, jFedPreferences);
    }

    public GetVersionTest(Server auth, JFedConnection.ProxyInfo proxyInfo, int version, GeniUserProvider geniUserProvider, JavaFXLogger logger, JFedConnectionProvider jFedConnectionProvider, JFedPreferences jFedPreferences) {
        this.auth = auth;

        StringBuilder description = new StringBuilder("GetVersion ");
        if (proxyInfo != null) {
            description.append("with proxy ");
        }
        description.append("@ ").append(auth.getName());


        this.description = description.toString();
        this.jFedPreferences = jFedPreferences;
        this.proxyInfo = proxyInfo;
        this.version = version;
        this.geniUserProvider = geniUserProvider;
        this.logger = logger;
        this.jFedConnectionProvider = jFedConnectionProvider;

        this.url = ApiInfo.findUrl(auth, new ApiInfo.Api(ApiInfo.ApiName.GENI_AM, version));
        if (url == null)
            throw new RuntimeException("No URL for AMv" + version + " at " + auth);
    }

    @Override
    public String getName() {
        if (proxyInfo == null)
            return String.format("[%s] %s (no proxy)", category, description);
        else
            return String.format("[%s] %s (with proxy)", category, description);
    }

    @Override
    public boolean isLoginRequired() {
        return true;
    }


    @Override
    public ConnectivityTestResult call() {
        final List<ApiCallDetails> callResults = new ArrayList<>();

        try {
            GeniUser geniUser = null;
            if (geniUserProvider.isUserLoggedIn())
                geniUser = geniUserProvider.getLoggedInGeniUser();
            else {

                return new ConnectivityTestResult(Status.FAILED, "no user logged in. Cannot test @ " + url);

            }
            if (geniUser == null) {
                return new ConnectivityTestResult(Status.FAILED, "no user logged in (logged in user is null). Cannot test @ " + url);

            }
            if (geniUser.getUserUrn() == null) {
                return new ConnectivityTestResult(Status.FAILED, "no user logged in (logged in user URN is null). Cannot test @ " + url);
            }
            logger.addResultListener(callResults::add);

            AbstractGeniAggregateManager am;
            if (version == 2)
                am = new AggregateManager2(logger, jFedPreferences);
            else
                am = new AggregateManager3(logger, jFedPreferences);

            SfaConnection con;
            if (jFedConnectionProvider instanceof SfaConnectionPool) {
                SfaConnectionPool conPool = (SfaConnectionPool) jFedConnectionProvider;
//                con = (SfaConnection) conPool.getConnectionByUrl(
//                        geniUser,
//                        url,
//                        null/*callback*/,
//                        new ApiInfo.Api(ApiInfo.ApiName.GENI_AM, version),
//                        proxyInfo /*overrides default proxy set in SfaConnectionPool (can also override to null, preventing proxy)*/);
                con = (SfaConnection) conPool.getConnectionByAuthority(
                        geniUser,
                        auth,
                        new ApiInfo.Api(ApiInfo.ApiName.GENI_AM, version),
                        false/*disableClientAuthentication*/,
                        proxyInfo /*overrides default proxy set in SfaConnectionPool (can also override to null, preventing proxy)*/);

                LOG.debug("GetVersionTest is creating connection to " + url + " for user " + geniUser.getUserUrnString() + " with proxy " + proxyInfo + " for AM version " + version);
            } else {
                throw new RuntimeException("guiModel.getConnectionProvider() is not SfaConnectionPool but " + jFedConnectionProvider.getClass().getName());
//                LOG.warn("guiModel.getConnectionProvider() is not SfaConnectionPool but "+guiModel.getConnectionProvider().getClass().getName());
//
//                //we can only use the default proxy set in the getConnectionProvider. We can't specify it ourselves
//                if (proxyInfo != null) {
//                    return new ConnectivityTestResult(Status.FAILED, "getConnectionProvider is not SfaConnectionPool, while proxyInfo != null. Cannot test @ " + url);
//                }
//                con = (SfaConnection) guiModel.getConnectionProvider().getConnectionByUrl(
//                        geniUser,
//                        url,
//                        null/*callback*/,
//                        new ApiInfo.Api(ApiInfo.ApiName.GENI_AM, version));
            }

            AbstractGeniAggregateManager.AggregateManagerReply<? extends AbstractGeniAggregateManager.AbstractVersionInfo>
                    reply =
                    am.getVersion(con);

            if (reply == null) {
                return new ConnectivityTestResult(Status.FAILED, "no reply to GetVersion @ " + url + " user=" + geniUser.getUserUrn());

            }
            if (reply.getGeniResponseCode() == null) {
                return new ConnectivityTestResult(Status.FAILED, "no geni response code replied to GetVersion @ " + url + " user=" + geniUser.getUserUrn());

            }
            if (!reply.getGeniResponseCode().isSuccess()) {
                return new ConnectivityTestResult(Status.FAILED, "reply to GetVersion was not success (" + reply.getGeniResponseCode() + ") @ " + url + " user=" + geniUser.getUserUrn());

            }
            if (reply.getValue() == null) {
                return new ConnectivityTestResult(Status.FAILED, "reply to GetVersion was success, but not version info was received @ " + url + " user=" + geniUser.getUserUrn());

            }
            AbstractGeniAggregateManager.AbstractVersionInfo versionInfo = reply.getValue();
            if (versionInfo.getApi() != version) {
                return new ConnectivityTestResult(Status.FAILED, "reply to GetVersion was success, but AM version was not " + version + " (was " + versionInfo.getApi() + ") @ " + url + " user=" + geniUser.getUserUrn());
            }

            //GetVersion ok
            return new ConnectivityTestResult(Status.SUCCEEDED, "GetVersion success @ " + url + " user=" + geniUser.getUserUrn() + " VersionInfo Received: " + versionInfo + "");

        } catch (Exception e) {
            StringBuilder callDetails = new StringBuilder();
            if (callResults.isEmpty())
                callDetails.append("no call details");
            for (ApiCallDetails callResult : callResults) {
                callDetails.append("Call Detail:\n").append(callResult.toString()).append("\n");
            }
            return new ConnectivityTestResult(Status.FAILED, "Failed to call GetVersion @ " + url + "  msg:" + e.getMessage() + " callDetails: " + callDetails.toString(), e);
        }

    }
}
