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

import com.atlassian.bitbucket.ServerException;
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.notes.GitNotes;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.stash.internal.scm.git.AbstractGitTest;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.util.Arrays;

import static com.atlassian.stash.internal.scm.git.GitFactoriesTestHelper.*;
import static org.hamcrest.Matchers.hasItems;
import static org.mockito.Mockito.when;

public class NotesCommandTest extends AbstractGitTest {

    @Autowired
    private GitCommandBuilderFactory builderFactory;
    @Repo
    private Repository repository;
    @Mock
    private ApplicationUser user;

    @Before
    public void setUpUser() throws Exception {
        when(user.getDisplayName()).thenReturn("Git Commit Test");
        when(user.getEmailAddress()).thenReturn("git@commit.test");
    }

    @Test
    @DirtiesRepo
    public void testAddAndShowNote() throws Exception {
        String note = "Mi bemolle";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).message(note).build().call();

        String output = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new StringHandler()).call();
        assertEquals(note, output);
    }

    @Test
    @DirtiesRepo
    public void testAddAndShowNotes() throws Exception {
        String note1 = "D flat", note2 = "G sharp";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).messages(note1, note2).build().call();

        Iterable<String> notes = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new QueueHandler()).call();
        assertThat(notes, hasItems(note1, note2));
    }

    @Test
    @DirtiesRepo
    public void testAddAndShowNotes2() throws Exception {
        String note1 = "D flat", note2 = "G sharp";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).messages(Arrays.asList(note1, note2)).build().call();

        Iterable<String> notes = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new QueueHandler()).call();
        assertThat(notes, hasItems(note1, note2));
    }

    @Test
    @DirtiesRepo
    public void testAddFromFile() throws Exception {
        File file = File.createTempFile("note-test", ".txt");
        try {
            Files.write("Do diesis", file, Charsets.US_ASCII);
            createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).file(file).build().call();
        } finally {
            FileUtils.deleteQuietly(file);
        }
    }

    @Test
    @DirtiesRepo
    public void testAddFromFile2() throws Exception {
        File file = File.createTempFile("note-test", ".txt");
        try {
            Files.write("Do diesis", file, Charsets.US_ASCII);
            createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).file(file.getPath()).build().call();
        } finally {
            FileUtils.deleteQuietly(file);
        }
    }

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testAddFromNonExistentFile() throws Exception {
        File file = File.createTempFile("note-test", ".txt");
        assertTrue(file.delete());
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).file(file).build().call();
    }

    @Test
    @DirtiesRepo
    public void testAddFromStdIn() throws Exception {
        String note = "Ga diesis";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user)
                .inputHandler(new StringInputHandler(note)).build().call();

        String output = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new StringHandler()).call();
        assertEquals(note, output);
    }

    @Test(expected = IllegalArgumentException.class)
    @DirtiesRepo
    public void testAddFromStdInWithoutInputHandler() throws Exception {
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).file("-").build().call();
    }

    @Test(expected = IllegalStateException.class)
    @DirtiesRepo
    public void testAddFromStdInButClearInputHandler() throws Exception {
        String note = "D flat";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user)
                .inputHandler(new StringInputHandler(note))
                .clearInputHandler().build().call();
    }

    @Test
    @DirtiesRepo
    public void testAddFromBothFileAndMessage() throws Exception {
        String note1 = "Ga diesis", note2 = "D sharp";
        File file = File.createTempFile("note-test", ".txt");
        try {
            Files.write(note1, file, Charsets.US_ASCII);
            createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).message(note2).file(file).build().call();

            Iterable<String> notes = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new QueueHandler()).call();
            assertThat(notes, hasItems(note1, note2));
        } finally {
            FileUtils.deleteQuietly(file);
        }
    }

    @Test(expected = IllegalStateException.class)
    @DirtiesRepo
    public void testAddWithoutSpecifyingFileOrMessage() throws Exception {
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).build().call();
    }

    @Test(expected = IllegalStateException.class)
    @DirtiesRepo
    public void testAddWithoutSpecifyingFileOrMessage2() throws Exception {
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).build(new StringHandler()).call();
    }

    @Test
    @DirtiesRepo
    public void testAddAndShowFromNonStandardLocation() throws Exception {
        String note = "Mi bemolle";
        String noteRef = "git/tests/notes";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).ref(noteRef)
                .message(note).build(new StringHandler()).call();

        assertCanShowFromNonStandardLocation(note, noteRef);
    }

    @Test
    @DirtiesRepo
    public void testAddAndShowAtNonStandardLocationFromFile() throws Exception {
        String note = "Mi bemolle";
        String noteRef = "git/tests/notes";
        File file = File.createTempFile("note-test", ".txt");
        try {
            Files.write(note, file, Charsets.US_ASCII);
            createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).ref(noteRef)
                    .file(file).build(new StringHandler()).call();

            assertCanShowFromNonStandardLocation(note, noteRef);
        } finally {
            FileUtils.deleteQuietly(file);
        }
    }

    @Test
    @DirtiesRepo
    public void testAddAndShowAtNonStandardLocationFromStdIn() throws Exception {
        String note = "Mi bemolle";
        String noteRef = "git/tests/notes";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).ref(noteRef)
                .inputHandler(new StringInputHandler(note)).build(new StringHandler()).call();

        assertCanShowFromNonStandardLocation(note, noteRef);
    }

    private void assertCanShowFromNonStandardLocation(String note, String noteRef) {
        try {
            createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new StringHandler()).call();
        } catch (CommandFailedException e) {
            // expected
        }
        String output = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").ref(noteRef).build(new StringHandler()).call();
        assertEquals(note, output);
    }

    @Test
    @DirtiesRepo
    public void testAddNotesAtDifferentLocations() throws Exception {
        String note1 = "Ga diesis", note2 = "D sharp";
        String note1Ref = "git/tests/notes";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).ref(note1Ref)
                .message(note1).build(new StringHandler()).call();
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user)
                .message(note2).build(new StringHandler()).call();

        String output1 = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").ref(note1Ref).build(new StringHandler()).call();
        assertEquals(note1, output1);
        String output2 = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new StringHandler()).call();
        assertEquals(note2, output2);
    }

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testOverwriteNote() throws Exception {
        String note1 = "D flat", note2 = "G sharp";
        try {
            createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).messages(note1).build().call();
        } catch (ServerException e) {
            fail(e.getMessage());
        }

        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).messages(note2).build().call();
    }

    @Test
    @DirtiesRepo
    public void testForceOverwriteNote() throws Exception {
        String note1 = "D flat", note2 = "G sharp";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).messages(note1).build().call();
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).force(true).messages(note2).build().call();

        String output = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new StringHandler()).call();
        assertEquals(note2, output);
    }

    @Test(expected = CommandFailedException.class)
    @DirtiesRepo
    public void testOverwriteNoteAtNonStandardLocation() throws Exception {
        String note1 = "Re", note2 = "Fa diesis";
        String noteRef = "git/tests/notes";
        try {
            createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).ref(noteRef).messages(note1).build().call();
        } catch (ServerException e) {
            fail(e.getMessage());
        }

        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).ref(noteRef).messages(note2).build().call();
    }

    @Test
    @DirtiesRepo
    public void testForceOverwriteNoteAtNonStandardLocation() throws Exception {
        String note1 = "G flat", note2 = "Sol bemolle";
        String noteRef = "git/tests/notes";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).ref(noteRef).messages(note1).build().call();
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).ref(noteRef).force(true).messages(note2).build().call();

        String output = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").ref(noteRef).build(new StringHandler()).call();
        assertEquals(note2, output);
    }

    @Test
    @DirtiesRepo
    public void testAddNoteWithCJKCharacters() throws Exception {
        String note = "測試 note";
        createBuilder().add("9c05f43f859375e392d90d23a13717c16d0fdcda").author(user).message(note).build(new StringHandler()).call();

        String output = createBuilder().show("9c05f43f859375e392d90d23a13717c16d0fdcda").build(new StringHandler()).call();
        assertEquals(note, output);
    }

    private GitNotes createBuilder() {
        return builderFactory.builder(repository).notes();
    }

}
