package com.amazonaws.workmail.demo;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
import microsoft.exchange.webservices.data.core.XmlElementNames;
import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
import microsoft.exchange.webservices.data.core.response.GetEventsResponse;
import microsoft.exchange.webservices.data.notification.GetEventsResults;
import microsoft.exchange.webservices.data.security.XmlNodeType;

import java.io.ByteArrayInputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;

/**
 * Data class holding the deserialized PushNotificationResponse
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
public final class PushNotificationResponse {

    private String version;

    private String watermark;

    private String subscriptionId;

    private String previousWatermark;

    // piggyback on GetEventsResponse. The internal structure is similar and we get the parsing for free
    private final GetEventsResponse response = new GetEventsResponse();

    public PushNotificationResponse(String inputXml) throws Exception {
        this.parseXml(inputXml);
    }

    private void parseXml(String inputXml) throws Exception {

        EwsServiceXmlReader reader = new EwsServiceXmlReader(
                new ByteArrayInputStream(inputXml.getBytes(StandardCharsets.UTF_8)), null);

        // read xml preamble
        reader.read(new XmlNodeType(XmlNodeType.START_DOCUMENT));
        reader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);

        // read soap header
        reader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPHeaderElementName);
        do {
            reader.read();

            if (reader.isStartElement(XmlNamespace.Types, XmlElementNames.ServerVersionInfo)) {
                this.version = reader.readAttributeValue("Version");
            }

            // Ignore anything else inside the SOAP header
        } while (!reader.isEndElement(XmlNamespace.Soap, XmlElementNames.SOAPHeaderElementName));

        //body
        reader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPBodyElementName);

        // the operation
        reader.readStartElement(XmlNamespace.Messages, "SendNotification");

        // exactly one response message inside
        reader.readStartElement(XmlNamespace.Messages, XmlElementNames.ResponseMessages);

        response.loadFromXml(reader, "SendNotificationResponseMessage");

        // subscriptionId is a protected field so behold Java reflection magic
        this.subscriptionId = getProtectedField(response.getResults(), "subscriptionId");
        this.watermark = getProtectedField(response.getResults(), "newWatermark");
        this.previousWatermark = getProtectedField(response.getResults(), "previousWatermark");

    }

    private String getProtectedField(GetEventsResults getEventsResults, String field) throws Exception {
        Field subscriptionIdField = getEventsResults.getClass().getDeclaredField(field);
        subscriptionIdField.setAccessible(true);
        return (String) subscriptionIdField.get(response.getResults());
    }
}