package com.atlassian.bitbucket.mesh.request;

import com.atlassian.bitbucket.mesh.rpc.MetadataKeys;
import com.atlassian.bitbucket.mesh.rpc.v1.CommitServiceGrpc;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGetCommitRequest;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGetCommitResponse;
import io.grpc.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.net.InetAddress;
import java.net.InetSocketAddress;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class RequestServerInterceptorTest {

    @Mock
    private ServerCall<RpcGetCommitRequest, RpcGetCommitResponse> call;
    @Mock
    private ServerCallHandler<RpcGetCommitRequest, RpcGetCommitResponse> handler;
    @InjectMocks
    private RequestServerInterceptor interceptor;
    @Mock
    private ServerCall.Listener<RpcGetCommitRequest> listener;
    private InetAddress localhost;
    @Mock
    private RequestManager requestManager;

    @Before
    @SuppressWarnings("unchecked")
    public void setup() throws Exception {
        localhost = InetAddress.getLocalHost();

        Attributes attributes = Attributes.newBuilder()
                .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, new InetSocketAddress(localhost, 42424))
                .build();

        when(call.getAttributes()).thenReturn(attributes);
        when(call.getMethodDescriptor()).thenReturn(CommitServiceGrpc.getGetCommitMethod());
        when(handler.startCall(any(RequestServerCall.class), any())).thenReturn(listener);
        when(requestManager.withContext(any(), notNull())).thenAnswer(invocation -> {
            RequestCallback<?> callback = invocation.getArgument(1);

            return callback.callWithRequest(mock(RequestContext.class)); //Context is ignored anyway
        });
    }

    @Test
    public void testInterceptCall() {
        Metadata metadata = new Metadata();
        metadata.put(MetadataKeys.KEY_REMOTE_CLIENT, "bitbucket");
        metadata.put(MetadataKeys.KEY_REMOTE_REQUEST, "request");
        metadata.put(MetadataKeys.KEY_REMOTE_USER, "user");

        ServerCall.Listener<RpcGetCommitRequest> actualListener = interceptor.interceptCall(call, metadata, handler);
        assertThat(actualListener, instanceOf(RequestServerCallListener.class));

        ArgumentCaptor<GrpcRequestDescriptor> descriptorCaptor = ArgumentCaptor.forClass(GrpcRequestDescriptor.class);
        verify(requestManager).withContext(descriptorCaptor.capture(), notNull());

        GrpcRequestDescriptor descriptor = descriptorCaptor.getValue();
        assertEquals("\"CommitService/GetCommit\"", descriptor.getAction());
        assertEquals("bitbucket", descriptor.getAgent());
        assertEquals("request", descriptor.getCorrelationId());
        assertEquals("grpc", descriptor.getProtocol());
        assertEquals(localhost.getHostAddress(), descriptor.getRemoteAddress());
        assertEquals("user", descriptor.getUser());
    }
}
