package com.atlassian.bitbucket.mesh.credentials;

import com.atlassian.bitbucket.mesh.execution.ExecutionContext;
import com.atlassian.bitbucket.mesh.execution.ExecutionManager;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcPrivateKeyCredentials;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcRemoteRepository;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcUsernamePasswordCredentials;
import com.google.protobuf.ByteString;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class SimpleCredentialsHelperTest {

    @InjectMocks
    private SimpleCredentialsHelper credentialsHelper;
    @Captor
    private ArgumentCaptor<Runnable> cleanupCaptor;
    @Mock
    private ExecutionContext executionContext;
    @Mock
    private ExecutionManager executionManager;

    @Before
    public void setUp() {
        when(executionManager.getContextOrThrow()).thenReturn(executionContext);
    }

    @Test
    public void testCreateTemporaryPrivateKey() throws IOException {
        Path path = credentialsHelper.createTemporaryPrivateKey(
                ByteString.copyFrom("this is a test", UTF_8));

        assertTrue(Files.isRegularFile(path));
        assertEquals("this is a test", new String(Files.readAllBytes(path), UTF_8));
        verify(executionContext).addCleanupCallback(cleanupCaptor.capture());

        // verify that the registered cleanup callback deletes the file
        cleanupCaptor.getValue().run();
        assertFalse(Files.exists(path));
    }

    @Test
    public void testGetCredentialsReturnsNullIfNoCredentialsSet() {
        assertNull(credentialsHelper.getCredentials(RpcRemoteRepository.newBuilder()
                .setUrl("some-url")
                .build()));
    }

    @Test
    public void testGetCredentialsUsernamePassword() {
        Credentials credentials = credentialsHelper.getCredentials(RpcRemoteRepository.newBuilder()
                .setUrl("some-url")
                .setUsernamePassword(RpcUsernamePasswordCredentials.newBuilder()
                        .setUsername(ByteString.copyFromUtf8("some-user"))
                        .setPassword(ByteString.copyFromUtf8("top-secret")))
                .build());

        assertEquals(new UsernamePasswordCredentials("some-user", "top-secret"), credentials);
        verifyNoInteractions(executionContext);
    }

    @Test
    public void testGetCredentialsPrivateKey() throws IOException {
        Credentials credentials = credentialsHelper.getCredentials(RpcRemoteRepository.newBuilder()
                .setUrl("some-url")
                .setPrivateKey(RpcPrivateKeyCredentials.newBuilder()
                        .setUsername(ByteString.copyFromUtf8("some-user"))
                        .setPassword(ByteString.copyFromUtf8("top-secret"))
                        .setPrivateKey(ByteString.copyFrom("private-key", UTF_8)))
                .build());

        assertThat(credentials, instanceOf(PrivateKeyCredentials.class));
        PrivateKeyCredentials privateKeyCredentials = (PrivateKeyCredentials) credentials;
        assertEquals("some-user", privateKeyCredentials.getUsername());
        assertEquals("top-secret", privateKeyCredentials.getPassphrase().orElse(null));

        Path keyPath = privateKeyCredentials.getPrivateKeyPath();
        assertTrue(Files.isRegularFile(keyPath));

        assertEquals("private-key", new String(Files.readAllBytes(keyPath), UTF_8));
        verify(executionContext).addCleanupCallback(cleanupCaptor.capture());

        // verify that the registered cleanup callback deletes the file
        cleanupCaptor.getValue().run();
        assertFalse(Files.exists(keyPath));
    }
}