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.fasterxml.jackson.core.JsonGenerator;
import net.logstash.logback.composite.AbstractJsonProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Arrays;

import static com.atlassian.bitbucket.mesh.logback.AccessLog.PHASE_END;

/**
 * A {@link AbstractJsonProvider} which when paired with access logging in the {@link AccessLog} formats
 * the fields for the "i" (start) and "o" (end) lines for the JSON access log. Rather than relying on the MDC, or any
 * thread-local state (which can easily end up cleared by the time "o" lines are written), this provider relies
 * on the {@link RequestContext} and the "i" or "o" value being provided as arguments on the {@link ILoggingEvent}
 * and then assembles the log entry accordingly.
 */
@CloudOnly
public class AccessLogJsonProvider extends AbstractJsonProvider<ILoggingEvent> {

    private static final Logger log = LoggerFactory.getLogger(AccessLogJsonProvider.class);

    public AccessLogJsonProvider() {
    }

    @Override
    public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException {
        Object[] arguments = event.getArgumentArray();
        if (arguments == null || arguments.length != 2 ||
                !(arguments[0] instanceof RequestContext && arguments[1] instanceof String)) {
            log.error("Unexpected arguments for access log: {}", Arrays.toString(arguments));
            return;
        }

        RequestContext ctx = (RequestContext) arguments[0];
        String phase = (String) arguments[1];

        generator.writeObjectField("phase", phase);

        generator.writeObjectField("remote_address", ctx.getRemoteAddress());
        generator.writeObjectField("protocol", ctx.getProtocol());
        generator.writeObjectField("id", ctx.getId());
        generator.writeObjectField("correlation_id", ctx.getCorrelationId());
        generator.writeObjectField("user", ctx.getUser());
        generator.writeObjectField("action", ctx.getAction());
        generator.writeObjectField("agent", ctx.getAgent());

        if (PHASE_END.equals(phase)) {
            generator.writeObjectField("result", ctx.getResult());
            generator.writeObjectField("messages_received", ctx.getMessagesReceived());
            generator.writeObjectField("messages_sent", ctx.getMessagesSent());
            generator.writeObjectField("bytes_received", ctx.getBytesReceived());
            generator.writeObjectField("bytes_sent", ctx.getBytesSent());
            generator.writeObjectField("labels", ctx.getLabels());
            generator.writeObjectField("duration", ctx.getDuration());
        }
    }
}
