package be.iminds.ilabt.jfed.lowlevel.fedmon;

import be.iminds.ilabt.jfed.log.Logger;
import be.iminds.ilabt.jfed.lowlevel.*;
import be.iminds.ilabt.jfed.lowlevel.connection.HttpConnection;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Fedmon
 */
public class Fedmon extends AbstractApi {
    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(Fedmon.class);

    public Fedmon(@Nonnull Logger logger, @Nonnull RetrySettings retrySettings, @Nonnull JFedPreferences jFedPreferences) {
        super(logger, retrySettings, new ApiInfo.Api(ApiInfo.ApiName.FED4FIRE_FEDMON, 1), jFedPreferences);
    }

    public Fedmon(@Nonnull Logger logger, @Nonnull JFedPreferences jFedPreferences) {
        super(logger, new ApiInfo.Api(ApiInfo.ApiName.FED4FIRE_FEDMON, 1), jFedPreferences);
    }

    public static String getApiName() {
        return "Federation Monitor API";
    }

    @Override
    public String getName() {
        return getApiName();
    }

    @Override
    protected boolean isBusyReply(XMLRPCCallDetails res) {
        return false;
    }

    @ApiMethod(order = 1, hint = "Get the latest testresults from Fedmon.")
    public FedmonReply<List<TestResult>> getTestResults(HttpConnection con,
                                                        @ApiMethodParameter(name = "testbedFilter", parameterType = ApiMethodParameterType.STRING, hint = "Name of the testbed for which to get results", required = false, guiDefault = "ple", guiDefaultOptional = true)
                                                                String testbedFilter,
                                                        @ApiMethodParameter(name = "testDefinitionNameFilter", parameterType = ApiMethodParameterType.STRING, hint = "Name of the test definition of the results (getVersion2, nodelogin, ping, ...)", required = false, guiDefault = "autonodelogin", guiDefaultOptional = true)
                                                                String testDefinitionNameFilter,
                                                        @ApiMethodParameter(name = "testbedUrnFilter", parameterType = ApiMethodParameterType.STRING, hint = "Urn of the testbed for which to get results", required = false, guiDefaultOptional = true)
                                                                String testbedUrnFilter) throws JFedException {
        assert con != null;
        Map<String, Object> methodParams = new HashMap<>();
        methodParams.put("last", "");
        if (testbedFilter != null) methodParams.put("testbed", testbedFilter);
        if (testDefinitionNameFilter != null) methodParams.put("testdefinitionname", testDefinitionNameFilter);
        if (testbedUrnFilter != null) methodParams.put("testbed_urn", testbedUrnFilter);

        LOG.debug("Fedmon getTestResults params: " + methodParams);

        String objectName = "result";
        String url = con.getServerUrl() + objectName;

        HttpCallDetails reply;
        try {
            reply = executeHttpCall(con, objectName, HttpVerb.GET, url,
                    null/*requestContentType*/, null/*requestContent*/,
                    "getTestResults", methodParams, methodParams);
        } catch (MalformedURLException e) {
            throw new JFedException("Error constructing relative URL \"" + url + "\"", e);
        }

        String jsonString = reply.getResultHttpContent();
        List<TestResult> testResultList = jsonToTestResults(jsonString);
        FedmonReply<List<TestResult>> res = new FedmonReply<>(reply, testResultList);
        logNonSfa(reply, res, "getTestResults", objectName, con, methodParams, null);
        return res;
    }

    private static String jsonStringHelper(JSONObject o, String key) {
        if (o.has(key))
            return o.getString(key);
        else
            return null;
    }

    private static List<TestResult> jsonToTestResults(String jsonString) {
        List<TestResult> res = null;
        Object root = new JSONTokener(jsonString).nextValue();
        if (root instanceof JSONArray) {
            JSONArray a = (JSONArray) root;
            res = new ArrayList<>();
            for (int i = 0; i < a.length(); i++) {
                JSONObject r = a.getJSONObject(i);
                String id = r.getString("id");
                String summary = r.getString("summary");
                Boolean testInstanceEnabled = r.getBoolean("testInstanceEnabled");
                JSONObject results = r.getJSONObject("results");

                String fixedNodeName = null;
                String fixedNodeUrn = null;
                String county = null;
                String latitude = null;
                String longitude = null;

                if (results != null) {
                    fixedNodeName = jsonStringHelper(results, "fixed_node_name");
                    fixedNodeUrn = jsonStringHelper(results, "fixed_node_urn");
                    county = jsonStringHelper(results, "location_county");
                    latitude = jsonStringHelper(results, "location_latitude");
                    longitude = jsonStringHelper(results, "location_longitude");
                }

                TestResult testResult =
                        new TestResult(id, summary, fixedNodeName, fixedNodeUrn, county, latitude, longitude, testInstanceEnabled);
                res.add(testResult);
            }
        }
        return res;
    }

    public static class FedmonReply<T> implements ApiCallReply<T> {
        private final T value;
        private final HttpCallDetails httpCallDetails;
        private final int httpCode;
        private final String httpCodeDescription;

        public FedmonReply(HttpCallDetails httpCallDetails, T value) {
            this.httpCallDetails = httpCallDetails;
            this.httpCode = httpCallDetails.getResultHttpStatusCode();
            this.httpCodeDescription = httpCallDetails.getResultHttpStatusLine();
            this.value = value;
        }

        @Override
        public GeniResponseCode getGeniResponseCode() {
            return new GeniResponseCode() {
                @Override
                public boolean isSuccess() {
                    return httpCode >= 200 && httpCode < 300;
                }

                @Override
                public boolean isBusy() {
                    return httpCode == 500 || httpCode == 503;
                } //NOTE: These codes CAN be busy, but are not always!

                @Override
                public int getCode() {
                    return httpCode;
                }

                @Override
                public String getDescription() {
                    return httpCodeDescription;
                }
            };
        }

        @Override
        public T getValue() {
            return value;
        }

        @Override
        public String getOutput() {
            return null;
        }

        @Override
        public Object getRawResult() {
            return null;
        }

        @Override
        public Object getRawValue() {
            return getValue();
        }

        public HttpCallDetails getHttpCallDetails() {
            return httpCallDetails;
        }

        private boolean busy;
    }
}
