package be.iminds.ilabt.jfed.call_log_output;

import be.iminds.ilabt.jfed.log.ApiCallDetails;
import be.iminds.ilabt.jfed.log.ApiCallIdGenerator;
import be.iminds.ilabt.jfed.lowlevel.ApiCallReply;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnection;
import be.iminds.ilabt.jfed.util.JSonHelper;
import be.iminds.ilabt.jfed.util.KeyUtil;
import be.iminds.ilabt.jfed.util.TextUtil;
import be.iminds.ilabt.jfed.util.XmlRpcPrintUtil;
import org.slf4j.LoggerFactory;

import javax.inject.Singleton;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.*;

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

    public SerializableApiCallDetailsFactory() {
    }

    public List<SerializableApiCallDetails> createFromApiCallDetails(Collection<ApiCallDetails> all) {
        Collection<ApiCallDetails> orig = new ArrayList<>(all);
        List<SerializableApiCallDetails> res = new ArrayList<>();
        for (ApiCallDetails a : orig)
            res.add(createFromApiCallDetails(a));
        return res;
    }

    public SerializableApiCallDetails createFromApiCallDetails(ApiCallDetails acd) {
        int id = acd.getId();
        String authorityName = acd.getServer() != null ? acd.getServer().getName() : null;
        String authorityUrn = acd.getServer() != null ? acd.getServer().getDefaultComponentManagerUrn() : null;
        String baseServerUrl = acd.getConnectionConfig().getServerUrlString();
        String callServerUrl = acd.getServerUrl();
        JFedConnection.ProxyInfo proxyInfo = acd.getConnectionConfig().getProxyInfo();

        String connectionSslAuthUserUrn;
        String connectionSslAuthUserCertificates;
        if (acd.getConnectionConfig().getDebugInfo() != null &&
                acd.getConnectionConfig().getDebugInfo().getUser() != null) {
            connectionSslAuthUserUrn = acd.getConnectionConfig().getDebugInfo().getUser().getUserUrnString();
            connectionSslAuthUserCertificates =
                    KeyUtil.x509certificateChainToPem(acd.getConnectionConfig().getDebugInfo().getUser().getClientCertificateChain());
        } else {
            connectionSslAuthUserUrn = null;
            connectionSslAuthUserCertificates = null;
        }

        //may be null if no basic HTTP authentication used
        String connectionBasicHttpAuthUsername = acd.getConnectionConfig().getLogin() == null ? null : acd.getConnectionConfig().getLogin().getUsername();
        String connectionAuthenticationMethod = acd.getConnectionConfig().getAuthentication()+"";
        String connectionProtocol = acd.getConnectionConfig().getProtocol()+"";
        String connectionType = acd.getConnectionConfig().getType()+"";
//        assert connectionType != null;
//        assert !connectionType.trim().isEmpty();

        String connectionId = acd.getConnectionId();

        String apiName = acd.getApiName();
        String javaMethodName = acd.getJavaMethodName();
        String geniMethodName = acd.getGeniMethodName();
        String methodParameters = acd.getMethodParameters().toString();

        String httpRequestStatusLine = acd.getHttpRequestStatusLine();
        String httpRequestHeaders = acd.getHttpRequestHeaders();
        String httpRequest = acd.getHttpRequest();
        String httpResponseStatusLine = acd.getHttpResponseStatusLine();
        String httpResponseHeaders = acd.getHttpResponseHeaders();
        String httpResponse = acd.getHttpResponse();

        String xmlRpcRequestJsonString = XmlRpcPrintUtil.xmlRpcObjectToString(acd.getXmlRpcRequest());
        String xmlRpcResponseJsonString = XmlRpcPrintUtil.xmlRpcObjectToString(acd.getXmlRpcResponse());


        String xmlRpcGeniResponseValue;
        String xmlRpcGeniResponseOutput;
        String xmlRpcGeniResponseCode;
        if (acd.getXmlRpcResponse() != null && (acd.getXmlRpcResponse() instanceof Map)) {
            Map ht = (Map) acd.getXmlRpcResponse();
            Object valueVal = ht.get("value");
            Object outputVal = ht.get("output");
            Object codeVal = ht.get("code");
            xmlRpcGeniResponseValue = (valueVal != null) ? XmlRpcPrintUtil.xmlRpcObjectToString(valueVal) : null;
            xmlRpcGeniResponseOutput =
                    (outputVal != null) ? XmlRpcPrintUtil.xmlRpcObjectToString(outputVal) : null;
            xmlRpcGeniResponseCode = (codeVal != null) ? XmlRpcPrintUtil.xmlRpcObjectToString(codeVal) : null;
        } else {
            xmlRpcGeniResponseValue = null;
            xmlRpcGeniResponseOutput = null;
            xmlRpcGeniResponseCode = null;
        }

        Integer geniResponseCode;
        String geniResponseCodeDescription;
        Boolean geniResponseCodeIsSuccess;
        String geniResponseOutput;
        if (acd.getReply() != null && acd.getReply().getGeniResponseCode() != null) {
            geniResponseCode = acd.getReply().getGeniResponseCode().getCode();
            geniResponseCodeDescription = acd.getReply().getGeniResponseCode().getDescription();
            geniResponseCodeIsSuccess = acd.getReply().getGeniResponseCode().isSuccess();
            geniResponseOutput = acd.getReply().getOutput();
        } else {
            geniResponseCode = null;
            geniResponseCodeDescription = null;
            geniResponseCodeIsSuccess = null;
            geniResponseOutput = null;
        }

        String rawValue;
        if (acd.getReply() != null && acd.getReply().getValue() != null) {
            if (acd.getReply().getRawValue() instanceof String) {
                rawValue = (String) acd.getReply().getRawValue();
            } else {
                rawValue = XmlRpcPrintUtil.anyObjectToJsonString(acd.getReply().getRawValue());
            }
        } else {
            rawValue = null;
        }

        Date startTime = acd.getStartTime();
        Date stopTime = acd.getStopTime();

        String protogeniSpewLogUrl = getProtogeniSpewLogUrl(acd.getReply());

        String exceptionString;
        if (acd.getException() != null) {
            exceptionString = TextUtil.exceptionToString(acd.getException());
        } else
            exceptionString = null;

        return new SerializableApiCallDetails(
                id,
                authorityName,
                authorityUrn,
                baseServerUrl,
                callServerUrl,
                proxyInfo,
                connectionSslAuthUserUrn,
                connectionBasicHttpAuthUsername,
                connectionId,
                connectionSslAuthUserCertificates,
                connectionAuthenticationMethod,
                connectionProtocol,
                connectionType,
                httpRequestStatusLine,
                httpRequestHeaders,
                httpRequest,
                httpResponseStatusLine,
                httpResponseHeaders,
                httpResponse,
                apiName,
                javaMethodName,
                geniMethodName,
                methodParameters,
                xmlRpcRequestJsonString,
                xmlRpcResponseJsonString,
                xmlRpcGeniResponseValue,
                xmlRpcGeniResponseOutput,
                xmlRpcGeniResponseCode,
                geniResponseCode,
                geniResponseCodeDescription,
                geniResponseCodeIsSuccess,
                geniResponseOutput,
                rawValue,
                startTime,
                stopTime,
                protogeniSpewLogUrl,
                exceptionString);
    }

    /*
    * protogeni_error_url "spewlogfile" is in the raw reply code, example:
    *
    *    <member><name>value</name><value><i4>0</i4></value></member>
    *    <member><name>output</name><value><string>No slice or aggregate here</string></value></member>
    *    <member><name>code</name><value>
    *      <struct>
    *         <member><name>protogeni_error_log</name><value><string>urn:publicid:IDN+emulab.net+log+48fcfd80d676d2a322dcfa02312d1fee</string></value></member>
    *         <member><name>protogeni_error_url</name><value><string>https://www.emulab.net/spewlogfile.php3?logfile=48fcfd80d676d2a322dcfa02312d1fee</string></value></member>
    *         <member><name>geni_code</name><value><i4>12</i4></value></member>
    *         <member><name>am_code</name><value><i4>12</i4></value></member>
    *         <member><name>am_type</name><value><string>protogeni</string></value></member>
    *      </struct></value>
    *    </member>
    *    </struct></value>
    * */
    private static String getProtogeniSpewLogUrl(ApiCallReply reply) {

        if (reply != null) {

            Object rawResult = reply.getRawResult();
            if (rawResult != null && rawResult instanceof Map) {
                Map rawMap = (Map) rawResult;
                Object urlObject = rawMap.get("protogeni_error_url");
                if (urlObject != null && urlObject instanceof String)
                    return (String) urlObject;

                Object rawCode = rawMap.get("code");
                if (rawCode != null && rawCode instanceof Map) {
                    Map codeMap = (Map) rawCode;
                    urlObject = codeMap.get("protogeni_error_url");
                    if (urlObject != null && urlObject instanceof String)
                        return (String) urlObject;
                }

            }
        }
        return null;
    }
}
