package be.iminds.ilabt.jfed.lowlevel.api.test;

import be.iminds.ilabt.jfed.log.Logger;
import be.iminds.ilabt.jfed.lowlevel.*;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.AggregateManagerWrapper;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.impl.AMv2Wrapper;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.impl.AMv3Wrapper;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnectionProvider;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import be.iminds.ilabt.jfed.testing.base.ApiTest;
import be.iminds.ilabt.jfed.util.TargetAuthority;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Map;
import java.util.List;

/**
 * SimpleTestAggregateManager2. One simple test of "Geni Aggregate Manager API v2"
 * <p/>
 * This is little more than a connectivity test and one basic test of XmlRpc and GetVersion.
 * <p/>
 * Use TestAggregateManager2 for a lot more tests of the "Geni Aggregate Manager API v2". (And also for more thorough tests of GetVersion)
 */
public class TestAnyGetVersion extends ApiTest {
    private AggregateManagerWrapper amWrapper;
    private CommonAMTest commonAMTest;

    @Inject
    public TestAnyGetVersion(Logger logger, TargetAuthority testedAuthority, GeniUserProvider geniUserProvider, JFedConnectionProvider connectionProvider, TestbedInfoSource testbedInfoSource, JFedPreferences jFedPreferences) {
        super(logger, testedAuthority, geniUserProvider, connectionProvider, testbedInfoSource, jFedPreferences);
    }

    public String getTestDescription() {
        return "Calls GetVersion on any available Aggregate manager for the given testbed. This is mainly useful to test connectivity.";
    }

    @Override
    public List<String> getOptConfKeys() {
        List<String> res = new ArrayList<>();

        res.add("am_version"); //"2", "3" or  "auto" (default: "auto")

        CommonAMTest.addOptionsForConnectionSshProxy(res);

        return res;
    }

    /**
     * Check for correctness of a AM2 XmlRpc result. Should be tested for each reply received.
     */
    public void testAM2CorrectnessXmlRpcResult(Map res) {
        Object code = res.get("code");
        Object value = res.get("value");
        Object output = res.get("output");
        assertNotNull(code, "testAM2CorrectnessXmlRpcResult code == null in " + res);
        assertNotNull(value, "testAM2CorrectnessXmlRpcResult value == null in " + res);
        assertInstanceOf(code, Map.class, "testAM2CorrectnessXmlRpcResult code is not Map in " + res + " (it is " + code.getClass().getName() + " with value \"" + code.toString() + "\")");

        Map codeHt = (Map) code;
        Object genicode = codeHt.get("geni_code");
        assertNotNull(genicode, "testAM2CorrectnessXmlRpcResult code does not contain \"geni_code\" in " + res);
        assertEquals(genicode.getClass(), Integer.class, "testAM2CorrectnessXmlRpcResult code.geni_code is not int in " + res);
        int genicodevalue = (Integer) genicode;

        //output should be present if code is not 0
        if (genicodevalue != 0) {
            assertNotNull(output, "testAM2CorrectnessXmlRpcResult: while geni_code is non success (" + genicodevalue + "), output == null in " + res);
            assertEquals(output.getClass(), String.class, "testAM2CorrectnessXmlRpcResult: while geni_code is non success (" + genicodevalue + "), output is not String (it is " + output.getClass().getName() + " with value \"" + output.toString() + "\") in \"+res+\"");
        } else {
            //this is a connectivity test, we don't warn here, we just make a note of it.
            if (output == null)
                note("testAM2CorrectnessXmlRpcResult: while geni_code is success (" + genicodevalue + "), output is ommited. This is allowed by the API.");
            else {
                if (output != null && !(output instanceof String))
                    note("testAM2CorrectnessXmlRpcResult: while geni_code is success (" + genicodevalue + "), output is not String (it is " + output.getClass().getName() + " with value \"" + output.toString() + "\"). This is allowed but not recommended by jFed.");
            }
        }
    }

    public void setUp() {
        String amVersion = getTestConfig().getProperty("am_version");
        if (amVersion == null || amVersion.trim().equalsIgnoreCase("auto")) {
            amWrapper = getAutomaticAggregateManagerWrapperFactory().create(logger, testedAuthority.getServerToConnect());
            //new AutomaticAggregateManagerWrapper(logger, getGeniUserProvider(), connectionProvider, jFedPreferences, testedAuthority.getServerToConnect());
        } else {
            if (amVersion.trim().equalsIgnoreCase("2")) {
                amWrapper = new AMv2Wrapper(logger, geniUserProvider, connectionProvider, jFedPreferences, testedAuthority.getServerToConnect());
            }
            if (amVersion.trim().equalsIgnoreCase("3")) {
                amWrapper = new AMv3Wrapper(logger, geniUserProvider, connectionProvider, jFedPreferences, testedAuthority.getServerToConnect());
            }
            if (amWrapper == null)
                throw new RuntimeException("Option \"am_version\" has unknown value (\""+amVersion+"\") should be \"auto\", \"2\" or \"3\"");
        }

        commonAMTest = new CommonAMTest(this, user.getUserAuthorityServer());
        commonAMTest.setupProxyForConnectionPool(getTestConfig());

        note("done");
    }

    private Map versionRawResult = null;

    @Test
    public void testAnyGetVersion() throws JFedException {
        assert amWrapper != null;

        versionRawResult = amWrapper.getVersion();

        testAM2CorrectnessXmlRpcResult(versionRawResult);

        assertNotNull(versionRawResult);

        Map<String, Object> replyHt = AbstractApi.apiSpecifiesMapStringToObject(versionRawResult);
        Map<String, Object> codeHt = AbstractApi.apiSpecifiesMapStringToObject(replyHt.get("code"));
        Integer genicodeHt = AbstractApi.apiSpecifiesInt(codeHt.get("geni_code"));

        assertEquals(genicodeHt, GeniAMResponseCode.GENIRESPONSE_SUCCESS.getCode(), "GeniResponse code is not SUCCESS (0)");

        Map<String, Object> valueHt = AbstractApi.apiSpecifiesMapStringToObject(replyHt.get("value"));
        Integer geniapiHt = AbstractApi.apiSpecifiesInt(replyHt.get("geni_api"));

        assertNotNull(geniapiHt);

        note("Test completed successfully.\nAll Done\n");
    }
}
