package com.atlassian.bitbucket.mesh.git.builder.branch;

import com.atlassian.bitbucket.mesh.git.exception.InvalidBranchNameException;
import com.atlassian.bitbucket.mesh.git.exception.RefNameConflictException;
import com.atlassian.bitbucket.mesh.process.ProcessSummary;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static com.atlassian.bitbucket.mesh.rpc.v1.RpcInvalidBranchName.Reason.*;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class CreateBranchCompletionHandlerTest {

    private CreateBranchCompletionHandler handler;
    private ProcessSummary summary;

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Before
    public void setUp() {
        handler = new CreateBranchCompletionHandler();
        summary = mock(ProcessSummary.class);
        when(summary.isCanceled()).thenReturn(true);
        when(summary.getCommandLine()).thenReturn("/some/git/command");
    }

    @Test
    public void testInvalidBranchName() {
        thrown.expect(InvalidBranchNameException.class);
        thrown.expect(hasProperty("branchName", equalTo("branchname")));
        thrown.expect(hasProperty("reason", equalTo(NAME_INVALID)));

        when(summary.getStderr()).thenReturn("fatal: 'branchname' is not a valid branch name.");
        handler.onComplete(summary);
    }

    @Test
    public void testDuplicateBranchName() {
        thrown.expect(InvalidBranchNameException.class);
        thrown.expect(hasProperty("branchName", equalTo("branchname")));
        thrown.expect(hasProperty("reason", equalTo(DUPLICATE)));

        when(summary.getStderr()).thenReturn("fatal: A branch named 'branchname' already exists.");
        handler.onComplete(summary);
    }

    @Test
    public void testInvalidBranchPoint() {
        thrown.expect(InvalidBranchNameException.class);
        thrown.expect(hasProperty("branchName", equalTo("24eb2cf:pom.xml")));
        // Different versions of Git output different errors, so either of these are possible
        thrown.expect(hasProperty("reason", oneOf(NAME_INVALID, START_POINT_INVALID)));

        when(summary.getStderr()).thenReturn("fatal: Not a valid branch point: '24eb2cf:pom.xml'.");
        handler.onComplete(summary);
    }

    @Test
    public void testNoSuchBranchObject() {
        thrown.expect(InvalidBranchNameException.class);
        thrown.expect(hasProperty("branchName", equalTo("nosuchstartpoint")));

        when(summary.getStderr()).thenReturn("fatal: Not a valid object name: 'nosuchstartpoint'.");
        handler.onComplete(summary);
    }

    // Ref conflict error message for v2.5.0-rc0
    @Test
    public void testBranchNameConflictCannotLockRef() {
        thrown.expect(RefNameConflictException.class);
        thrown.expect(hasProperty("refName", equalTo("refs/heads/master/v1.0")));
        thrown.expect(hasProperty("conflictName", equalTo("refs/heads/master")));

        when(summary.getStderr()).thenReturn("fatal: cannot lock ref 'refs/heads/master/v1.0': 'refs/heads/master' exists; cannot create 'refs/heads/master/v1.0'");
        handler.onComplete(summary);
    }

    // Ref conflict error message for v2.4.3 - 2.4.5
    @Test
    public void testBranchNameConflictCannotLockRefOldMessage() {
        thrown.expect(RefNameConflictException.class);
        thrown.expect(hasProperty("refName", equalTo("refs/heads/master/v1.0")));
        thrown.expect(hasProperty("conflictName", equalTo("refs/heads/master")));

        when(summary.getStderr()).thenReturn("fatal: Cannot lock ref 'refs/heads/master/v1.0': 'refs/heads/master' exists; cannot create 'refs/heads/master/v1.0'");
        handler.onComplete(summary);
    }

    // Ref conflict error message for pre Git 2.4.3
    @Test
    public void testBranchNameConflictDirFileLooseRefs() {
        thrown.expect(RefNameConflictException.class);
        thrown.expect(hasProperty("refName", equalTo("refs/heads/release/v1.0")));
        thrown.expect(hasProperty("conflictName", nullValue()));

        when(summary.getStderr()).thenReturn("error: unable to resolve reference refs/heads/release/v1.0: Not a directory");
        handler.onComplete(summary);
    }

    // Ref conflict error message for pre Git 2.4.3
    @Test
    public void testBranchNameConflictFileDirLooseRefs() {
        thrown.expect(RefNameConflictException.class);
        thrown.expect(hasProperty("refName", equalTo("refs/heads/bugfix")));
        thrown.expect(hasProperty("conflictName", nullValue()));

        when(summary.getStderr()).thenReturn("error: there are still refs under 'refs/heads/bugfix'");
        handler.onComplete(summary);
    }

    // Ref conflict error message for pre Git 2.4.3
    @Test
    public void testBranchNameConflictPackedRefs() {
        thrown.expect(RefNameConflictException.class);
        thrown.expect(hasProperty("refName", equalTo("refs/heads/master/v1.0")));
        thrown.expect(hasProperty("conflictName", equalTo("refs/heads/master")));

        when(summary.getStderr()).thenReturn("error: 'refs/heads/master' exists; cannot create 'refs/heads/master/v1.0'");
        handler.onComplete(summary);
    }
}