package com.atlassian.stash.internal.commit;

import com.atlassian.bitbucket.annotation.Dao;
import com.atlassian.bitbucket.annotation.DataSet;
import com.atlassian.bitbucket.content.DiffFileType;
import com.atlassian.bitbucket.content.DiffSegmentType;
import com.atlassian.stash.internal.DataSets;
import com.atlassian.stash.internal.comment.AbstractDiffCommentAnchorDaoTest;
import com.atlassian.stash.internal.comment.CommentCounts;
import com.atlassian.stash.internal.comment.InternalComment;
import com.atlassian.stash.internal.comment.InternalDiffCommentAnchor;
import com.atlassian.stash.internal.content.InternalChangeLocation;
import org.junit.Test;

import java.util.Map;

@DataSet({DataSets.USERS, DataSets.PROJECTS, DataSets.REPOSITORIES, DataSets.COMMENTS,
        DataSets.COMMIT_DISCUSSIONS, DataSets.COMMIT_DISCUSSION_COMMENTS})
public class HibernateCommitDiscussionCommentAnchorDaoTest extends AbstractDiffCommentAnchorDaoTest {

    @Dao
    private HibernateCommitDiscussionCommentAnchorDao dao;

    @Test
    public void testCreate() {
        InternalComment comment = loadComment(9L);
        InternalCommitDiscussion discussion = loadDiscussion(2L);

        InternalCommitDiscussionCommentAnchor anchor = new InternalCommitDiscussionCommentAnchor.Builder(discussion)
                .comment(comment)
                .line(11, DiffSegmentType.ADDED, DiffFileType.TO)
                .path("caviar-webapp/pom.xml")
                .toHash("ed4eac02cd99d55161866f316ef2618a06609ed8")
                .build();
        InternalDiffCommentAnchor created = dao.create(anchor);
        assertNotNull(created.getId());
        assertEquals(created.getId(), anchor.getId());
    }

    @Test
    public void testGetAnchorByCommentRoot() {
        InternalCommitDiscussion discussion = loadDiscussion(3L);

        InternalCommitDiscussionCommentAnchor anchor = dao.getAnchorByComment(discussion, 1L);
        assertNotNull(anchor);
    }

    @Test
    public void testGetAnchorByCommentReply() {
        InternalComment comment = loadComment(2L);
        assertEquals(1L, comment.getRoot().getId());

        InternalCommitDiscussion discussion = loadDiscussion(3L);

        InternalCommitDiscussionCommentAnchor anchor = dao.getAnchorByComment(discussion, 2L);
        assertNotNull(anchor);
    }

    @Test
    public void testGetAnchorByCommentDeepReply() {
        InternalComment comment = loadComment(3L);
        assertEquals(1L, comment.getRoot().getId());

        InternalCommitDiscussion discussion = loadDiscussion(3L);

        InternalCommitDiscussionCommentAnchor anchor = dao.getAnchorByComment(discussion, 3L);
        assertNotNull(anchor);
    }

    @Test
    public void testGetAnchorByCommentNone() {
        InternalCommitDiscussion discussion = loadDiscussion(3L);

        InternalCommitDiscussionCommentAnchor anchor = dao.getAnchorByComment(discussion, 4L);
        assertNull(anchor);
    }

    @Test
    public void testGetCountsByLocation() {
        Map<InternalChangeLocation, CommentCounts> counts = dao.getCountsByLocation(loadDiscussion(1L));
        assertEquals(3, counts.size());
        assertCounts(counts, "dao-impl/src/main/java/com/atlassian/stash/internal/pull/HibernatePullRequestDao.java", 1, 0);
        assertCounts(counts, "plugins/rest/pom.xml", 1, 0);
        assertCounts(counts, "pom.xml", 1, 0);
    }

    @Test
    @DataSet({DataSets.USERS, DataSets.PROJECTS, DataSets.REPOSITORIES, DataSets.PULL_REQUESTS, DataSets.COMMENTS,
            DataSets.COMMIT_DISCUSSIONS, DataSets.COMMIT_DISCUSSION_COMMENTS, DataSets.COMMIT_DISCUSSION_EXTRA_COMMENTS})
    public void testGetCountsByLocationWithPullRequest() {
        Map<InternalChangeLocation, CommentCounts> counts = dao.getCountsByLocation(loadDiscussion(4L), 1L);
        assertEquals(1, counts.size());
        assertCounts(counts, "CommitLevelReviewComment.java", 1, 0);
    }

    //Note: From and to hashes are _always_ set on commit discussion comments, so searching here with a null from or
    //      to hash will always return no results
    @Test
    public void testSearch() {
        InternalCommitDiscussion discussion = loadDiscussion(3L);

        //HibernateCommitDiscussionCommentAnchorDao.createAnchorCriteria should automatically apply the commit and
        //parent IDs from the discussion, so searching with a vanilla criteria should match
        CommitDiscussionAnchorSearchCriteria.Builder builder = new CommitDiscussionAnchorSearchCriteria.Builder(discussion);
        assertAnchorIds(dao.search(builder.build()), 1L);
        //Searching with a path should also work
        assertAnchorIds(dao.search(builder.path("SomeClass.java").build()), 1L);
        //If the from hash is invalid, no results are returned
        assertAnchorIds(dao.search(builder.fromHash("This is invalid").build()));
        //Back to a valid from hash
        assertAnchorIds(dao.search(builder.fromHash("96f7d3ea46f612cd009031628ece1c47c5b1c942").build()), 1L);
    }

    @Test
    public void testSearchWithMergeCommit() {
        InternalCommitDiscussion discussion = loadDiscussion(1L);

        //Discussion 1 emulates a merge commit, so it has anchors on two parents. The base criteria should just
        //search in the first parent; it should not match anchors from both
        CommitDiscussionAnchorSearchCriteria.Builder builder = new CommitDiscussionAnchorSearchCriteria.Builder(discussion);
        assertAnchorIds(dao.search(builder.build()), 2L, 4L); //3 is on the "second" parent

        //Explicitly specifying the second parent's hash should switch the returned anchors
        builder.fromHash("46cd407ce04a7ca352eee294601b005f70e5c562");
        assertAnchorIds(dao.search(builder.build()), 3L);
    }

    @Test
    @DataSet({DataSets.USERS, DataSets.PROJECTS, DataSets.REPOSITORIES, DataSets.PULL_REQUESTS, DataSets.COMMENTS,
            DataSets.COMMIT_DISCUSSIONS, DataSets.COMMIT_DISCUSSION_COMMENTS, DataSets.COMMIT_DISCUSSION_EXTRA_COMMENTS})
    public void testSearchWithPullRequest() {
        InternalCommitDiscussion discussion = loadDiscussion(4L);

        CommitDiscussionAnchorSearchCriteria.Builder builder = new CommitDiscussionAnchorSearchCriteria.Builder(discussion)
                .fromHash("478d8c6a3d65386241bda42f6529e913e2233831");
        assertTrue(dao.search(builder.build()).isEmpty());
        assertAnchorIds(dao.search(builder.pullRequestScopedId(1L).build()), 201L);
        assertTrue(dao.search(builder.pullRequestScopedId(2L).build()).isEmpty());
    }

    @Test
    public void testSearchWithRootCommit() {
        InternalCommitDiscussion discussion = loadDiscussion(2L);

        //This test also verifies that comments are ordered by line number before date
        assertAnchorIds(dao.search(new CommitDiscussionAnchorSearchCriteria.Builder(discussion).build()), 6L, 5L);
    }

    @Test(expected = UnsupportedOperationException.class)
    public void testUpdate() {
        dao.update(dao.getById(1L));
    }

    private InternalCommitDiscussion loadDiscussion(long id) {
        return (InternalCommitDiscussion) getCurrentSession().load(InternalCommitDiscussion.class, id);
    }
}
