package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderFactory;
import com.atlassian.bitbucket.mesh.git.ref.RefUpdate;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.codec.binary.Hex;
import org.junit.Before;
import org.junit.Test;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

import static com.atlassian.bitbucket.mesh.repository.RefHasher.NULL_HASH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.mock;

public class DefaultRefHasherTest {

    private Random random;
    private DefaultRefHasher refHasher;

    @Before
    public void setup() {
        random = new Random(0);
        refHasher = new DefaultRefHasher(mock(GitProcessBuilderFactory.class));
    }

    @Test
    public void testCalculateHashAddingABranchChangesHash() {
        String tx0 = refHasher.calculateHash(ImmutableSet.of(RefUpdate.ofAdd("refs/heads/master", randomCommitHash())));
        String tx1 = refHasher.calculateHash(ImmutableSet.of(RefUpdate.ofAdd("refs/heads/a", randomCommitHash())));
        String h0 = refHasher.combineHashes(NULL_HASH, tx0);
        String h1 = refHasher.combineHashes(h0, tx1);

        assertNotEquals(NULL_HASH, h0);
        assertNotEquals(h0, h1);
    }

    @Test
    public void testCalculateHashCreateThenDeleteRestoresOldHash() {
        String commitOne = randomCommitHash();
        String commitTwo = randomCommitHash();
        String tx0 = refHasher.calculateHash(ImmutableSet.of(RefUpdate.ofAdd("refs/heads/master", commitOne)));
        String tx1 = refHasher.calculateHash(ImmutableSet.of(RefUpdate.ofAdd("refs/heads/a", commitTwo)));
        String tx2 = refHasher.calculateHash(ImmutableSet.of(RefUpdate.ofDelete("refs/heads/a", commitTwo)));
        String h0 = refHasher.combineHashes(NULL_HASH, tx0);
        String h1 = refHasher.combineHashes(h0, tx1);
        String h2 = refHasher.combineHashes(h1, tx2);

        assertNotEquals(h0, h1);
        assertEquals(h0, h2);
    }

    @Test
    public void testCalculateHashIgnoresHead() {
        String hash = refHasher.calculateHash(ImmutableSet.of(RefUpdate.ofAdd("HEAD", randomCommitHash())));

        assertEquals(NULL_HASH, hash);
    }

    private String randomCommitHash() {
        try {
            MessageDigest sha1 = MessageDigest.getInstance("SHA1");
            byte[] bytes = new byte[sha1.getDigestLength()];
            random.nextBytes(bytes);
            return Hex.encodeHexString(sha1.digest(bytes));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}