package be.iminds.ilabt.jfed.log;

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.lowlevel.ApiCallReply;
import be.iminds.ilabt.jfed.lowlevel.connection.ConnectionConfig;
import be.iminds.ilabt.jfed.util.IOUtils;
import be.iminds.ilabt.jfed.util.XmlRpcPrintUtil;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.util.*;

/**
 * ApiCallDetails contains everything about an API call.
 * This includes HTTP and XmlRpc sent and received data,
 * the interpreted APICallReply object, info about the method that did the call,
 * including it's parameters, etc...
 */

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


    private final int id;

    private final Service service;
    private final Server server;
    private final String serverUrl;

    private final ConnectionConfig connectionConfig;
    private final String connectionId;

    private final String httpRequestStatusLine;
    private final String httpRequestHeaders;
    private final String httpRequest;
    private final String httpResponseStatusLine;
    private final String httpResponseHeaders;
    private final String httpResponse;

    private final List xmlRpcRequest;
    private final Object xmlRpcResponse;


    private final String apiName;
    private final String javaMethodName;
    private final String geniMethodName;
    private final Map<String, Object> methodParameters;
    private final Date startTime;
    private final Date stopTime;
    private final Throwable exception;
    private ApiCallReply reply;


    public ApiCallDetails(Service service,
                          Server server,
                          ConnectionConfig connectionConfig,
                          String serverUrl,
                          String connectionId,
                          String apiName, String javaMethodName, String geniMethodName,
                          ApiCallReply reply,

                          String httpRequestStatusLine, String httpRequestHeaders, String httpRequest,
                          String httpResponseStatusLine, String httpResponseHeaders, String httpResponse,

                          Vector xmlRpcRequest, Object xmlRpcReply,
                          Map<String, Object> methodParameters, Date startTime, Date stopTime, Throwable exception) {
        this(service, server, connectionConfig, serverUrl, connectionId, apiName, javaMethodName, geniMethodName, reply, httpRequestStatusLine, httpRequestHeaders, httpRequest, httpResponseStatusLine, httpResponseHeaders, httpResponse, xmlRpcRequest != null ? new ArrayList<>(xmlRpcRequest) : null, xmlRpcReply, methodParameters, startTime, stopTime, exception);
    }

    /**
     * @param service          (if applicable, otherwise null)
     * @param server           (if applicable, otherwise null)
     * @param connectionConfig config of the connection, including base URL
     * @param serverUrl        actual URL called over the connection. can be another URL than the base URL!
     * @param apiName          API Name
     * @param javaMethodName   may be null
     * @param geniMethodName   Name of GENI API method call
     * @param reply            may be null
     * @param httpRequest      HTTP request-object
     * @param httpResponse     HTTP response-object
     */
    public ApiCallDetails(Service service,
                          Server server,
                          ConnectionConfig connectionConfig,
                          String serverUrl,
                          String connectionId,
                          String apiName, String javaMethodName, String geniMethodName,
                          ApiCallReply reply,

                          String httpRequestStatusLine, String httpRequestHeaders, String httpRequest,
                          String httpResponseStatusLine, String httpResponseHeaders, String httpResponse,

                          List xmlRpcRequest, Object xmlRpcReply,
                          Map<String, Object> methodParameters, Date startTime, Date stopTime, Throwable exception) {
        assert connectionConfig != null;

        this.id = ApiCallIdGenerator.getNextId();
        this.connectionConfig = connectionConfig;
        this.serverUrl = serverUrl;
        this.connectionId = connectionId;
        this.service = service;
        this.server = server;
        this.apiName = apiName;
        this.javaMethodName = javaMethodName;
        this.geniMethodName = geniMethodName;
        this.reply = reply;

        this.httpRequestStatusLine = httpRequestStatusLine;
        this.httpRequestHeaders = httpRequestHeaders;
        this.httpRequest = httpRequest;

        this.httpResponseStatusLine = httpResponseStatusLine;
        this.httpResponseHeaders = httpResponseHeaders;
        this.httpResponse = httpResponse;

        this.xmlRpcRequest = xmlRpcRequest;
        this.xmlRpcResponse = xmlRpcReply;

        if (methodParameters == null)
            this.methodParameters = Collections.emptyMap();
        else
            this.methodParameters = Collections.unmodifiableMap(methodParameters);
        this.startTime = startTime;
        this.stopTime = stopTime;
        this.exception = exception;

    }

    /**
     * This ID is only unique within one instance of an application using the jFed library.
     *
     * @return Unique ID for this request.
     */
    public int getId() {
        return id;
    }

    public String getConnectionId() {
        return connectionId;
    }

    /**
     * @return Service corresponding to the server+url this call was made to. Can be null
     */
    public Service getService() {
        return service;
    }

    /**
     * @return Server this call was made to. Can be null
     */
    public Server getServer() {
        return server;
    }

    @Deprecated
    public Server getAuthority() {
        return server;
    }

    public String getServerComponentManagerUrnString() {
        return server == null ? null : server.getDefaultComponentManagerUrn();
    }

    /**
     * @return Server URL to which this call was made
     */
    public String getServerUrl() {
        return serverUrl;
    }

    public ConnectionConfig getConnectionConfig() {
        return connectionConfig;
    }

    /**
     * @return Internal name of the function that was called
     */
    public String getApiName() {
        return apiName;
    }


    /**
     * @return The full XmlRpc-object that was sent
     */
    public Object getXmlRpcRequest() {
        return xmlRpcRequest;
    }

    /**
     * @return The full XmlRpc-object that was received
     */
    public Object getXmlRpcResponse() {
        return xmlRpcResponse;
    }

    /**
     * The method parameters-map contains all parameters that were used by the jFed library to construct
     * the request.
     *
     * @return Map with method parameters.
     */
    public Map<String, Object> getMethodParameters() {
        return methodParameters;
    }

    /**
     * The method in the jFed library that performed this call.
     *
     * @return The internal jFed method name. May return null
     */
    public String getJavaMethodName() {
        return javaMethodName;
    }

    /**
     * The name of the API method that was called.
     *
     * @return The API method name.
     */
    public String getGeniMethodName() {
        return geniMethodName;
    }

    /**
     * The reply
     *
     * @return may return null
     */
    @Nullable
    public ApiCallReply getReply() {
        return reply;
    }

    /**
     * @return The HTTP Status line that was sent
     */
    public String getHttpRequestStatusLine() {
        return httpRequestStatusLine;
    }

    /**
     * @return The HTTP headers that were sent
     */
    public String getHttpRequestHeaders() {
        return httpRequestHeaders;
    }

    /**
     * @return The contents of the HTTP request
     */
    public String getHttpRequest() {
        return httpRequest;
    }

    /**
     * @return The HTTP response status that was received
     */
    public String getHttpResponseStatusLine() {
        return httpResponseStatusLine;
    }


    /**
     * @return The HTTP headers that were received
     */
    public String getHttpResponseHeaders() {
        return httpResponseHeaders;
    }

    /**
     * @return The full contents HTTP response that was received
     */
    public String getHttpResponse() {
        return httpResponse;
    }


    public String toString() {
        String responseCode = "no reply";
        if (reply != null && reply.getGeniResponseCode() != null)
            responseCode = reply.getGeniResponseCode().toString();
        return "Result{" + '\n' +
                "serverUrl='" + serverUrl + '\'' + '\n' +
                ", proxyInfo='" + connectionConfig.getProxyInfo() + '\'' + '\n' +
                ", connectionBasicHttpAuthUsername='" + (
                connectionConfig.getLogin() == null || connectionConfig.getLogin().getUsername() == null ? "null"
                        : connectionConfig.getLogin().getUsername()) + '\'' + '\n' +
                ", connectionSslAuthUser='" + (
                connectionConfig.getDebugInfo() == null || connectionConfig.getDebugInfo().getUser() == null ? "null"
                        : connectionConfig.getDebugInfo().getUser().getUserUrnString()) + '\'' + '\n' +
                (exception == null ? ""
                        : ", exception='" + exception.getMessage() + " -> Stacktrace:\n" + IOUtils.exceptionToStacktraceString(exception) + '\'' + '\n') +
                ", apiName='" + apiName + '\'' + '\n' +
                ", javaMethodName='" + javaMethodName + '\'' + '\n' +
                ", geniMethodName='" + geniMethodName + '\'' + '\n' +
                ", reply.getGeniResponseCode()=" + responseCode + '\n' +
                ", httpRequest='" + httpRequest + '\'' + '\n' +
                ", httpResponseStatusLine='" + httpResponseStatusLine + '\'' + '\n' +
                ", httpResponse='" + httpResponse + '\'' + '\n' +
                '}';
    }

    /**
     * @return The moment the API call was started
     */
    public Date getStartTime() {
        return startTime;
    }

    /**
     * @return The moment that the API call completed
     */
    public Date getStopTime() {
        return stopTime;
    }

    /**
     * @return The exception that was received while making this call.
     * This field will only be non-null when an exception occured during the execution.
     */
    public Throwable getException() {
        return exception;
    }

    /**
     * Convenience method which converts the XmlRpc request-object into a corresponding JSON-format
     *
     * @return A JSON-formatted string of the request
     */
    public String getXmlRpcRequestJsonString() {
        return XmlRpcPrintUtil.xmlRpcObjectToString(xmlRpcRequest);
    }

    /**
     * Convenience method which converts the XmlRpc response-object into a corresponding JSON-format
     *
     * @return A JSON-formatted string of the response
     */
    public String getXmlRpcResponseJsonString() {
        return XmlRpcPrintUtil.xmlRpcObjectToString(xmlRpcResponse);
    }
}
