package com.atlassian.bitbucket.mesh.logback;

import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.atlassian.bitbucket.mesh.request.RequestContext;
import com.atlassian.bitbucket.mesh.request.RequestManager;

import java.util.function.Function;

/**
 * A {@link ClassicConverter} which uses the {@link RequestManager} to include a summary of the request in log messages.
 */
public class RequestContextConverter extends ClassicConverter {

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

    private Function<RequestContext, String> converter;
    private volatile RequestManager requestManager;

    public RequestContextConverter() {
        converter = ctx -> formatRequest(ctx) + " " + formatMessages(ctx);
    }

    @Override
    public String convert(ILoggingEvent ignored) {
        RequestManager requestManager = this.requestManager;
        if (requestManager == null) {
            requestManager = LogbackUtils.getRequestManager(getContext());
            if (requestManager == null) {
                return "-";
            }

            this.requestManager = requestManager;
        }

        return requestManager.getContext()
                .map(converter)
                .orElse("-");
    }

    @Override
    public void start() {
        String option = getFirstOption();
        if ("id".equalsIgnoreCase(option)) {
            //This converter is called a lot, so we don't want to check a property to decide how to convert the
            //RequestContext on each call. Instead, since we need to pass a Function to Stream.map anyway, we
            //use a class field for that and simply replace it once to switch to ID-only
            converter = RequestContext::getId;
        }

        super.start();
    }

    /**
     * Formats the messages sent and received during the request.
     *
     * @param ctx the request context
     * @return the formatted message counts
     */
    private static String formatMessages(RequestContext ctx) {
        return new StringBuilder(10)
                .append("(>")
                .append(ctx.getMessagesReceived())
                .append(" <")
                .append(ctx.getMessagesSent())
                .append(')')
                .toString();
    }

    /**
     * Formats details about the request, omitting request properties that aren't set.
     *
     * @param ctx the request context
     * @return the formatted request details
     */
    private static String formatRequest(RequestContext ctx) {
        String[] fields = new String[]{
                ctx.getUser(),
                ctx.getId(),
                ctx.getCorrelationId(),
                ctx.getRemoteAddress(),
                ctx.getAction()
        };

        StringBuilder builder = new StringBuilder();
        for (String field : fields) {
            if (field == null) {
                continue;
            }

            if (builder.length() > 0) {
                builder.append(' ');
            }
            builder.append(field);
        }

        return builder.length() == 0 ? "-" : builder.toString();
    }
}
