package com.atlassian.bitbucket.mesh.logback;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggingEvent;
import com.atlassian.bitbucket.mesh.request.RequestContext;
import com.atlassian.bitbucket.mesh.request.RequestManager;
import com.fasterxml.jackson.core.JsonGenerator;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

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

import static com.atlassian.bitbucket.mesh.logback.RequestContextJsonProvider.OBJ_REQUEST_MANAGER;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class RequestContextJsonProviderTest {
    
    private RequestContextJsonProvider provider;

    @Mock
    private LoggerContext loggerContext;
    @Mock
    private JsonGenerator jsonGenerator;
    @Mock
    private RequestContext requestContext;
    @Mock
    private RequestManager requestManager;

    @Before
    public void setup() {
        provider = new RequestContextJsonProvider();
        provider.setContext(loggerContext);
        provider.start();
    }

    @Test
    public void testWrite() throws IOException {
        when(loggerContext.getObject(eq(OBJ_REQUEST_MANAGER))).thenReturn(requestManager);
        when(requestManager.getContext()).thenReturn(Optional.of(requestContext));

        when(requestContext.getAction()).thenReturn("action");
        when(requestContext.getCorrelationId()).thenReturn("correlationId");
        when(requestContext.getId()).thenReturn("id");
        when(requestContext.getMessagesReceived()).thenReturn(1);
        when(requestContext.getMessagesSent()).thenReturn(3);
        when(requestContext.getRemoteAddress()).thenReturn("address");
        when(requestContext.getUser()).thenReturn("user");

        provider.writeTo(jsonGenerator, new LoggingEvent());

        verify(jsonGenerator).writeObjectField("action", "action");
        verify(jsonGenerator).writeObjectField("correlation_id", "correlationId");
        verify(jsonGenerator).writeObjectField("id", "id");
        verify(jsonGenerator).writeObjectField("messages_received", 1);
        verify(jsonGenerator).writeObjectField("messages_sent", 3);
        verify(jsonGenerator).writeObjectField("remote_address", "address");
        verify(jsonGenerator).writeObjectField("user", "user");

        verifyWroteRequestTopLevelField();

        verifyNoMoreInteractions(jsonGenerator);
    }

    @Test
    public void testWriteNoFields() throws IOException {
        when(loggerContext.getObject(eq(OBJ_REQUEST_MANAGER))).thenReturn(requestManager);
        when(requestManager.getContext()).thenReturn(Optional.of(requestContext));

        provider.writeTo(jsonGenerator, new LoggingEvent());

        verify(jsonGenerator).writeObjectField("action", null);
        verify(jsonGenerator).writeObjectField("correlation_id", null);
        verify(jsonGenerator).writeObjectField("id", null);
        verify(jsonGenerator).writeObjectField("messages_received", 0);
        verify(jsonGenerator).writeObjectField("messages_sent", 0);
        verify(jsonGenerator).writeObjectField("remote_address", null);
        verify(jsonGenerator).writeObjectField("user", null);

        verifyWroteRequestTopLevelField();

        verifyNoMoreInteractions(jsonGenerator);
    }

    @Test
    public void testWriteWithoutRequestManager() throws IOException {
        provider.writeTo(jsonGenerator, new LoggingEvent());
        verifyNoMoreInteractions(jsonGenerator);
    }

    @Test
    public void testWriteWithoutRequest() throws IOException {
        when(loggerContext.getObject(eq(OBJ_REQUEST_MANAGER))).thenReturn(requestManager);
        when(requestManager.getContext()).thenReturn(Optional.empty());

        provider.writeTo(jsonGenerator, new LoggingEvent());
        verifyNoMoreInteractions(jsonGenerator);

        verify(loggerContext).getObject(eq(OBJ_REQUEST_MANAGER));
        verify(requestManager).getContext();
    }

    private void verifyWroteRequestTopLevelField() throws IOException {
        InOrder order = inOrder(jsonGenerator);
        order.verify(jsonGenerator).writeObjectFieldStart("request");
        order.verify(jsonGenerator).writeEndObject();
    }
}
