package it.com.atlassian.stash;

import com.atlassian.bitbucket.hamcrest.SimpleTypeSafeMatcher;
import com.atlassian.bitbucket.test.ClasspathResourceRule;
import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.bitbucket.test.PullRequestTestHelper;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.webdriver.bitbucket.element.Avatar;
import com.atlassian.webdriver.bitbucket.element.CommitMetadata;
import com.atlassian.webdriver.bitbucket.element.CommitList;
import com.atlassian.webdriver.bitbucket.element.activity.ActivityItem;
import com.atlassian.webdriver.bitbucket.element.activity.GeneralCommentActivity;
import com.atlassian.webdriver.bitbucket.page.*;
import it.com.atlassian.stash.rules.LoginRule;
import org.hamcrest.Matcher;
import org.junit.ClassRule;
import org.junit.Test;

import static com.atlassian.bitbucket.test.UserTestHelper.deleteAvatar;
import static com.atlassian.bitbucket.test.UserTestHelper.uploadAvatar;
import static it.com.atlassian.stash.rules.SessionCleanupRule.KeepSession;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;

/**
 * Checks that uploading an avatar makes Stash uses the local avatar in:
 * <ul>
 *     <li>the pull request overview page and comments,</li>
 *     <li>the commit list,</li>
 *     <li>the commit page,</li>
 *     <li>the blame view in the file view.</li>
 * </ul>
 */
@KeepSession
public class UserAvatarLinkingTest extends BaseBrowserFuncTest {

    private static final String AVATAR_RESOURCE = "/com/atlassian/stash/test/avatar/test-avatar.png";
    private static final String PROJECT = DefaultFuncTestData.getProject1();
    private static final String REPOSITORY = DefaultFuncTestData.getProject1Repository1();
    private static final String SOURCE_BRANCH = DefaultFuncTestData.getProject1Repository1BasicBranchingBranch();
    private static final String TARGET_SOURCE = DefaultFuncTestData.getProject1Repository1MasterBranch();

    @ClassRule
    public static final ClasspathResourceRule resources = new ClasspathResourceRule();

    @ClassRule
    public static final LoginRule loginOnce = new LoginRule();

    /**
     * Checks the following avatars are switched from Gravatars to local avatars:
     * <ul>
     *     <li>the author of the pull request,</li>
     *     <li>the author of a general comment.</li>
     * </ul>
     */
    @Test
    public void testPullRequestAvatars() throws Exception {
        String username = "test-pr-avatars";
        testContext.user(username).projectPermission(PROJECT, username, Permission.PROJECT_WRITE);
        deleteAvatar(username);

        // create the PR and add a general comment to it
        PullRequestTestHelper pullRequest = testContext.pullRequest(new PullRequestTestHelper.Builder(
                username, username, "Avatar Linking test", String.format("A mention [^%s]", username),
                PROJECT, REPOSITORY, SOURCE_BRANCH,
                PROJECT, REPOSITORY, TARGET_SOURCE));
        pullRequest.addGeneralComment("A comment");

        // visit the PR, check the authors of the PR and general comment have an avatar pointing to Gravatar (Gravatar support is enabled by default)
        verifyPullRequestAuthors(pullRequest, is(gravatarFor(username)));

        // upload an avatar for the author
        uploadAvatar(username, resources.get(AVATAR_RESOURCE));

        // re-check that the pull request overview and check the avatar is now local
        verifyPullRequestAuthors(pullRequest, is(localAvatarFor(username)));

        // TODO: when deleting the local avatar, check the URL goes back to Gravatar
    }

    @Test
    public void testCommitListAndPage() throws Exception {
        // verify the authors in the commit list, page and blame
        verifyCommitAuthors("409a2180fd95b744df8d817e770b7f8c853f78dc", equalTo("Michael Heemskerk"), is(gravatarFor("Michael Heemskerk")));

        String username = "test-linking";

        // create a Stash user matching the commit's author
        testContext.user(username, "Test Linking", username, "mheemskerk@atlassian.com");
        deleteAvatar(username);

        // re-verify with the new user (note that a different revision must be used because the blame data is cached per commit ID)
        verifyCommitAuthors("c2608f5dff150e2b26b4b68d9e22369581b39b0c", equalTo("Test Linking"), is(gravatarFor(username)));

        // upload an avatar for the author of the commit
        uploadAvatar(username, resources.get(AVATAR_RESOURCE));

        // re-verify with the new avatar
        verifyCommitAuthors("2e10dd2d1d5eea9291b296e78312e8a703964a95", equalTo("Test Linking"), is(localAvatarFor(username)));
    }

    private void verifyBlameAuthor(FileSourcePage page, int line, Matcher<String> displayNameMatcher) {
        assertThat(page.showBlame().get(line).getAuthor(), displayNameMatcher);
    }

    private void verifyCommitAuthors(String commitId, Matcher<String> displayNameMatcher, Matcher<Avatar> avatarMatcher) {
        CommitListPage list = BITBUCKET.visit(CommitListPage.class, PROJECT, REPOSITORY, SOURCE_BRANCH); // commit list only accepts named refs
        verifyCommitAuthor(list, 0, displayNameMatcher, avatarMatcher);
        ChangesetUnifiedDiffPage cs = BITBUCKET.visit(ChangesetUnifiedDiffPage.class, PROJECT, REPOSITORY, commitId);
        verifyCommitAuthor(cs, displayNameMatcher, avatarMatcher);
        FileSourcePage file = BITBUCKET.visit(FileSourcePage.class, PROJECT, REPOSITORY, "out_of_order/file.txt", commitId);
        verifyBlameAuthor(file, 0, displayNameMatcher);
    }

    private void verifyCommitAuthor(CommitListPage list, int position, Matcher<String> displayNameMatcher, Matcher<Avatar> avatarMatcher) {
        CommitList.CommitListEntry commit = list.getCommitList().getRows().get(position);
        assertThat(commit.getAuthor(), displayNameMatcher);
        assertThat(commit.getAuthorAvatar(), avatarMatcher);
    }

    private void verifyCommitAuthor(CommitPage cs, Matcher<String> displayNameMatcher, Matcher<Avatar> avatarMatcher) {
        CommitMetadata metadata = cs.getMetadata();
        assertThat(metadata.getAuthorName(), displayNameMatcher);
        assertThat(metadata.getAuthorAvatar(), avatarMatcher);
    }

    private void verifyPullRequestAuthors(PullRequestTestHelper pullRequest, Matcher<Avatar> avatarMatcher) {
        PullRequestOverviewPage page = BITBUCKET.visit(PullRequestOverviewPage.class, pullRequest.getProjectKey(), pullRequest.getRepoSlug(), pullRequest.getId());
        assertThat(page.getPullRequestAuthorAvatar(), avatarMatcher);
        ActivityItem activity = page.getActivityItems().get(0);
        assertThat(activity, instanceOf(GeneralCommentActivity.class));
        assertThat(activity.getAvatar(), avatarMatcher);
    }

    private static Matcher<Avatar> gravatarFor(final String username) {
        return new AvatarMatcher(username, "http://www.gravatar.com/");
    }

    private static Matcher<Avatar> localAvatarFor(final String username) {
        return new AvatarMatcher(username, DefaultFuncTestData.getBaseURL());
    }

    private static class AvatarMatcher extends SimpleTypeSafeMatcher<Avatar> {
        private final String username;
        private final String baseUrl;

        public AvatarMatcher(String username, String baseUrl) {
            super("an avatar for user [" + username + "] whose URL starts with: " + baseUrl);
            this.username = username;
            this.baseUrl = baseUrl;
        }

        @Override
        protected boolean matchesSafely(Avatar avatar) {
            return avatar.getUsername().equals(username) && avatar.getUrl().startsWith(baseUrl);
        }
    }
}
