package com.atlassian.bitbucket.mesh.logback;

import ch.qos.logback.classic.spi.ILoggingEvent;
import com.atlassian.bitbucket.mesh.annotation.CloudOnly;
import com.atlassian.bitbucket.mesh.request.RequestContext;
import com.atlassian.bitbucket.mesh.request.RequestManager;
import com.fasterxml.jackson.core.JsonGenerator;
import net.logstash.logback.composite.AbstractJsonProvider;

import java.io.IOException;
import java.util.Optional;

/**
 * A {@link AbstractJsonProvider} which uses the {@link RequestManager} to include a `request` field for context in
 * JSON log messages.
 */
@CloudOnly
public class RequestContextJsonProvider extends AbstractJsonProvider<ILoggingEvent> {

    static final String OBJ_REQUEST_MANAGER = RequestManager.class.getName();

    private volatile RequestManager requestManager;

    public RequestContextJsonProvider() {
    }

    @Override
    public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException {
        RequestContext ctx = getRequestContext().orElse(null);
        if (ctx == null) {
            // Don't include a `request` field if the context is not available
            return;
        }

        // Write a `request` field to nest the context fields in
        generator.writeObjectFieldStart("request");

        generator.writeObjectField("messages_received", ctx.getMessagesReceived());
        generator.writeObjectField("messages_sent", ctx.getMessagesSent());
        generator.writeObjectField("user", ctx.getUser());
        generator.writeObjectField("id", ctx.getId());
        generator.writeObjectField("correlation_id", ctx.getCorrelationId());
        generator.writeObjectField("remote_address", ctx.getRemoteAddress());
        generator.writeObjectField("action", ctx.getAction());

        // Close the `request` field
        generator.writeEndObject();
    }

    private Optional<RequestContext> getRequestContext() {
        RequestManager requestManager = this.requestManager;
        if (requestManager == null) {
            requestManager = LogbackUtils.getRequestManager(getContext());
            if (requestManager == null) {
                return Optional.empty();
            }

            this.requestManager = requestManager;
        }

        return requestManager.getContext();
    }
}
