package com.atlassian.stash.internal.scm.git.command.config;

import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.CommandFailedException;
import com.atlassian.bitbucket.scm.annotation.DirtiesRepo;
import com.atlassian.bitbucket.scm.annotation.Repo;
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory;
import com.atlassian.bitbucket.scm.git.command.config.GitConfig;
import com.atlassian.stash.internal.scm.git.AbstractGitTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import static com.atlassian.stash.internal.scm.git.GitFactoriesTestHelper.StringHandler;

public class ConfigCommandTest extends AbstractGitTest {

    @Autowired
    private GitCommandBuilderFactory builderFactory;
    @Repo
    private Repository repository;

    @Test
    @DirtiesRepo
    public void testSetAndGetProperty() {
        testSetAndGetProperty("test.section.property", "value-123");
    }

    @Test
    @DirtiesRepo
    public void testSetAndGetPropertyTwice() throws Exception {
        testSetAndGetProperty("test.simple.property", "a");
        // This will error if trying to get more than one property (ie if --add is used)
        testSetAndGetProperty("test.simple.property", "b");
    }

    @Test
    @DirtiesRepo
    public void testSetAndGetPropertyWithoutSubsection() {
        testSetAndGetProperty("test.property", "value-123");
    }

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testSetPropertyWithoutSection() throws Exception {
        createBuilder().set("property", "value-123").build().call();
    }

    @Test
    public void testGetUnknownProperty() throws Exception {
        testGetPropertyFails("test.property-" + System.currentTimeMillis());
    }

    /* section names are case insensitive */

    @Test
    @DirtiesRepo
    public void testGetPropertyWithSectionCasingChange() {
        String value = "value-123";
        createBuilder().set("test.property", value).build().call();
        assertEquals(value, createBuilder().get("test.property").build(new StringHandler()).call());
        assertEquals(value, createBuilder().get("TEST.property").build(new StringHandler()).call());
        assertEquals(value, createBuilder().get("Test.property").build(new StringHandler()).call());
        assertEquals(value, createBuilder().get("TeST.property").build(new StringHandler()).call());
    }

    /* subsection names are case sensitive */

    @Test
    @DirtiesRepo
    public void testGetPropertyWithSubSectionCasingChange() {
        String value = "value-123";
        createBuilder().set("test.casing.property", value).build().call();

        assertEquals(value, createBuilder().get("test.casing.property").build(new StringHandler()).call());
        testGetPropertyFails("test.CASING.property");
    }

    /* variables names are case-insensitive */

    @Test
    @DirtiesRepo
    public void testGetPropertyWithPropertyCasingChange() {
        String value = "value-123";
        createBuilder().set("test.casing.property", value).build().call();

        assertEquals(value, createBuilder().get("test.casing.property").build(new StringHandler()).call());
        assertEquals(value, createBuilder().get("test.casing.PROPERTY").build(new StringHandler()).call());
        assertEquals(value, createBuilder().get("test.casing.Property").build(new StringHandler()).call());
        assertEquals(value, createBuilder().get("test.casing.ProPERty").build(new StringHandler()).call());
    }

    /* section names only allows alphanumeric characters , '-' and '.' */

    @Test
    @DirtiesRepo
    public void testRenameSection() throws Exception {
        String value = "value-123";
        createBuilder().set("test.section.property1", value).build().call();
        createBuilder().set("test.section.property2", value).build().call();

        createBuilder().renameSection("test.section", "test.new").build().call();

        testGetPropertyFails("test.section.property1");
        testGetPropertyFails("test.section.property2");

        assertEquals(value, createBuilder().get("test.new.property1").build(new StringHandler()).call());
        assertEquals(value, createBuilder().get("test.new.property2").build(new StringHandler()).call());
    }

    @Test(expected = CommandFailedException.class)
    public void testRenameNonExistentSection() throws Exception {
        createBuilder().renameSection("test.section", "test.new").build().call();
    }

    @Test
    @DirtiesRepo
    public void testSetAndGetPropertyHavingSectionWithDash() throws Exception {
        testSetAndGetProperty("test-123.section.property", "value-123");
    }

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testSetPropertyHavingSectionWithDiacritics() throws Exception {
        createBuilder().set("téŝt.section.property", "value-123").build().call();
    }

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testSetPropertyHavingSectionWithCJKCharacters() throws Exception {
        createBuilder().set("測試.section.property", "value-123").build().call();
    }

    /* subsection names can have every characters except new lines, unescaped doublequote and backslashes */

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testSetPropertyHavingSubSectionWithNewlines() throws Exception {
        createBuilder().set("test.si\nple.property", "value-123").build().call();
    }

    @Test
    @DirtiesRepo
    public void testSetAndGetPropertyHavingSubSectionWithQuotes() throws Exception {
        testSetAndGetProperty("test.si\"m\"ple.property", "value-123");
    }

    @Test
    @DirtiesRepo
    public void testSetAndGetPropertyHavingSubSectionWithDiacritics() throws Exception {
        testSetAndGetProperty("test.sîḿpḽe.property", "value-123");
    }

    @Test
    @DirtiesRepo
    public void testSetAndGetPropertyHavingSubSectionWithCJKCharacters() throws Exception {
        testSetAndGetProperty("test.測試.property", "value-123");
    }

    /* variables names can not only have alphanumeric characters and '-' */

    @Test
    @DirtiesRepo
    public void testGetAndSetPropertyHavingPropertyWithDash() throws Exception {
        testSetAndGetProperty("test.section.property-key", "value-123");
    }

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testSetPropertyHavingPropertyWithDiacritics() throws Exception {
        createBuilder().set("test.section.prõpérty", "value-123").build().call();
    }

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testSetPropertyHavingPropertyWithCJKCharacters() throws Exception {
        createBuilder().set("test.section.測試", "value-123").build().call();
    }

    /* values can be over multiple lines */

    @Test
    @DirtiesRepo
    public void testGetAndSetMultipleLineValue() throws Exception {
        testSetAndGetProperty("test.section.property", "value\nabc\n123");
    }

    /* initial and trailing whitespaces in values are discarded */

    @Test
    @DirtiesRepo
    public void testGetAndSetValueWithTrailingWhiteSpaces() throws Exception {
        createBuilder().set("test.section.property", "   property   value          ").build().call();
        assertEquals("property   value", createBuilder().get("test.section.property").build(new StringHandler()).call());
    }

    /* whether non-alphanumeric characters are supported in values is not specified */

    @Test
    @DirtiesRepo
    public void testGetAndSetValueWithDiacritics() throws Exception {
        testSetAndGetProperty("test.section.property", "prõpérty value");
    }

    @Test
    @DirtiesRepo
    public void testGetAndSetValueWithCJKCharacters() throws Exception {
        testSetAndGetProperty("test.section.property", "測試 value");
    }

    @Test
    @DirtiesRepo
    public void testUnsetValue() throws Exception {
        String key = "test.section.property";
        createBuilder().set(key, "value-123").build().call();
        createBuilder().unset(key).build().call();

        testGetPropertyFails(key);
    }

    @Test
    @DirtiesRepo
    public void testUnsetAll() throws Exception {
        String key = "test.section.property";
        createBuilder().set(key, "value-123").build().call();
        createBuilder().set(key, "value-123-bis").build().call();
        createBuilder().unsetAll(key).build().call();

        testGetPropertyFails(key);
    }


    private void testGetPropertyFails(String key) {
        try {
            createBuilder().get(key).build().call();
            fail("should have failed to get property with " + key);
        } catch (CommandFailedException e) {
            // expected
        }
    }

    private void testSetAndGetProperty(String key, String value) {
        createBuilder().set(key, value).build().call();
        assertEquals(value, createBuilder().get(key).build(new StringHandler()).call());
    }

    private GitConfig createBuilder() {
        return builderFactory.builder(repository).config();
    }
}
