package ca.pjer.logback;

import ch.qos.logback.classic.spi.ILoggingEvent;

import java.nio.charset.Charset;

import com.amazonaws.services.logs.model.InputLogEvent;

abstract class Worker {

    private AwsLogsAppender awsLogsAppender;

    Worker(AwsLogsAppender awsLogsAppender) {
        this.awsLogsAppender = awsLogsAppender;
    }

    AwsLogsAppender getAwsLogsAppender() {
        return awsLogsAppender;
    }

    public synchronized void start() {
    }

    public synchronized void stop() {
    }

    public abstract void append(ILoggingEvent event);

    // See https://github.com/pierredavidbelanger/logback-awslogs-appender/issues/6
    private static final int MAX_EVENT_SIZE = 262144;

    InputLogEvent asInputLogEvent(ILoggingEvent event) {
        InputLogEvent inputLogEvent = new InputLogEvent().withTimestamp(event.getTimeStamp())
                .withMessage(awsLogsAppender.getLayout().doLayout(event));

        if (eventSize(inputLogEvent) > MAX_EVENT_SIZE) {
            awsLogsAppender
                    .addWarn(String.format("Log message exceeded Cloudwatch Log's limit of %d bytes", MAX_EVENT_SIZE));
            trimMessage(inputLogEvent, MAX_EVENT_SIZE);
        }

        return inputLogEvent;
    }

    // See http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
    private static final int EVENT_SIZE_PADDING = 26;
    private static final Charset EVENT_SIZE_CHARSET = Charset.forName("UTF-8");

    static final int eventSize(InputLogEvent event) {
        return event.getMessage().getBytes(EVENT_SIZE_CHARSET).length + EVENT_SIZE_PADDING;
    }

    private static final String ELLIPSIS = "...";

    private static final void trimMessage(InputLogEvent event, int eventSize) {
        int trimmedMessageSize = eventSize - EVENT_SIZE_PADDING - ELLIPSIS.getBytes(EVENT_SIZE_CHARSET).length;
        byte[] message = event.getMessage().getBytes(EVENT_SIZE_CHARSET);

        String unsafeTrimmed = new String(message, 0, trimmedMessageSize + 1, EVENT_SIZE_CHARSET);
        // The last character might be a chopped UTF-8 character
        String trimmed = unsafeTrimmed.substring(0, unsafeTrimmed.length() - 1);

        event.setMessage(trimmed + ELLIPSIS);
    }
}