package it.com.atlassian.stash.visual;

import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.bitbucket.test.PermissionsTestHelper;
import com.atlassian.bitbucket.test.PullRequestTestHelper;
import com.atlassian.bitbucket.test.RepositoryTestHelper;
import com.atlassian.bitbucket.user.Permission;
import com.atlassian.webdriver.bitbucket.element.DiffCommentForm;
import com.atlassian.webdriver.bitbucket.element.DiffFileContent;
import com.atlassian.webdriver.bitbucket.element.UnifiedDiffFileContent;
import com.atlassian.webdriver.bitbucket.page.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;

public class VisualRegressionSmokeTest extends BaseVisualRegressionTest {

    private static final String USER_WITH_CREATE_PROJ = "can-create-proj";
    private static final String USER_WITHOUT_CREATE_PROJ = "no-create";

    private static final String PROJECT_KEY = "QA_TEST";
    private static final String PROJECT_NAME = "QA Test Project";
    private static final String REPO_SLUG = "qa-resources";
    private static final String PROJECT_DESCRIPTION = "Project Description";

    private static final String USER_PROJ_ADMIN = "proj-admin";
    private static final String USER_PROJ_WRITE = "proj-write";
    private static final String USER_PROJ_READ = "proj-read";
    private static final String USER_REPO_READ = "repo-read";

    // Pull Requests
    protected static final String SOURCE_COMMIT = "ff851cdafd5c9bda5b19d151c5f638f13c2b2f6e";
    protected static final String SOURCE_BRANCH = "branch_that_has_same_text_file_modified_on_both_clean_merge_src";
    protected static final String TARGET_BRANCH = "branch_that_has_same_text_file_modified_on_both_clean_merge_trgt";
    protected static final String ADMINISTRATOR = "Administrator";

    protected static final String PR_PROJECT_KEY = "PR_TEST";
    protected static final String PR_REPO_SLUG = "pr-resources";
    protected static final String PR_PROJECT_NAME = "PR Test Project";
    protected static final String PR_PROJECT_DESCRIPTION = "PR Test Project for Visual Regressions";
    protected static final String PULL_REQUEST_TITLE = "Visual Regressions Pull Request Test";
    protected static final String PULL_REQUEST_DESCRIPTION = "Pull request description text.";

    private static final String GIT_PR_RESOURCES_ZIP = "git/pull-requests.zip";

    protected static final String PULL_REQUEST_CREATOR_USERNAME = "prtestuser";
    protected static final String PULL_REQUEST_CREATOR_DISPLAY_NAME = "PR Test User <script>alert('xss')</script>";
    protected static final String PULL_REQUEST_CREATOR_PASSWORD = "password";
    protected static final String PULL_REQUEST_CREATOR_EMAIL_ADDRESS = "pr_creator@fake-domain.org";

    protected static PullRequestTestHelper.Builder pullRequestBuilder = new PullRequestTestHelper.Builder(
            DefaultFuncTestData.getAdminUser(),
            DefaultFuncTestData.getAdminPassword(),
            PULL_REQUEST_TITLE, PULL_REQUEST_DESCRIPTION,
            PR_PROJECT_KEY, PR_REPO_SLUG, SOURCE_BRANCH,
            PR_PROJECT_KEY, PR_REPO_SLUG, TARGET_BRANCH
    );

    protected static PullRequestTestHelper pullRequest;

    protected static final String SOURCE_BRANCH_CONFLICT = "branch_that_has_same_text_file_modified_on_both_src";
    protected static final String TARGET_BRANCH_CONFLICT = "branch_that_has_same_text_file_modified_on_both_trgt";

    protected static PullRequestTestHelper.Builder conflictedPullRequestBuilder = new PullRequestTestHelper.Builder(
            DefaultFuncTestData.getAdminUser(),
            DefaultFuncTestData.getAdminPassword(),
            PULL_REQUEST_TITLE, PULL_REQUEST_DESCRIPTION,
            PR_PROJECT_KEY, PR_REPO_SLUG, SOURCE_BRANCH,
            PR_PROJECT_KEY, PR_REPO_SLUG, TARGET_BRANCH
    );

    protected static PullRequestTestHelper conflictedPullRequest;

    @BeforeClass
    public static void createTestContext() throws Exception {
        String base64 = Base64.encodeBase64String(IOUtils.toByteArray(VisualRegressionSmokeTest.class.getResourceAsStream("/com/atlassian/stash/visual/test-project-avatar.png")));
        String PROJECT_AVATAR = "data:image/png;base64," + base64;

        getClassTestContext().user(USER_WITH_CREATE_PROJ).globalPermission(USER_WITH_CREATE_PROJ, Permission.PROJECT_CREATE);
        getClassTestContext().user(USER_WITHOUT_CREATE_PROJ).globalPermission(USER_WITHOUT_CREATE_PROJ, Permission.LICENSED_USER);

        getClassTestContext().project(PROJECT_KEY, PROJECT_NAME, PROJECT_DESCRIPTION, PROJECT_AVATAR)
                .user(USER_PROJ_ADMIN)
                .projectPermission(PROJECT_KEY, USER_PROJ_ADMIN, Permission.PROJECT_ADMIN)
                .user(USER_PROJ_WRITE)
                .projectPermission(PROJECT_KEY, USER_PROJ_WRITE, Permission.PROJECT_WRITE)
                .user(USER_PROJ_READ)
                .projectPermission(PROJECT_KEY, USER_PROJ_READ, Permission.PROJECT_READ);

        getClassTestContext().repository(PROJECT_KEY, REPO_SLUG, new ClassPathResource(RepositoryTestHelper.QA_RESOURCES_REPOSITORY));

        // pull requests
        getClassTestContext().project(PR_PROJECT_KEY, PR_PROJECT_NAME, PR_PROJECT_DESCRIPTION, PROJECT_AVATAR)
                .repository(PR_PROJECT_KEY, PR_REPO_SLUG, new ClassPathResource(GIT_PR_RESOURCES_ZIP))
                .user(PULL_REQUEST_CREATOR_USERNAME, PULL_REQUEST_CREATOR_DISPLAY_NAME, PULL_REQUEST_CREATOR_PASSWORD, PULL_REQUEST_CREATOR_EMAIL_ADDRESS)
                .projectPermission(PR_PROJECT_KEY, PULL_REQUEST_CREATOR_USERNAME, Permission.PROJECT_READ);

        pullRequest = getClassTestContext().pullRequest(pullRequestBuilder);

    }

    @BeforeClass
    public static void killAnnoyingBanner() {
        removeStpBanner();
    }

    @AfterClass
    public static void destroyRepository() throws Exception {
        RepositoryTestHelper.destroyRepository(PROJECT_KEY, REPO_SLUG);
    }

    @Test
    public void testEmptyProjectListPage() {
        loginAsRegularUser(BitbucketHomePage.class);
        STASH.visit(ProjectListPage.class);
        assertUIMatches("project-list-empty");
    }

    @Test
    public void testPopulatedProjectListPage() {
        //Let him see all the projects
        PermissionsTestHelper.setGlobalPermissionForUser(USER_WITH_CREATE_PROJ, Permission.ADMIN.name());
        loginAs(USER_WITH_CREATE_PROJ, USER_WITH_CREATE_PROJ, BitbucketHomePage.class);
        STASH.visit(ProjectListPage.class);
        assertUIMatches("project-list-populated");
    }

    @Test
    public void testProjectOverviewPage() {
        ProjectOverviewPage page = loginAsAdmin(ProjectOverviewPage.class, PROJECT_KEY);
        page.getRepositoryTable();
        assertUIMatches("project-overview");
    }

    @Test
    public void testFileBrowserPage() {
        loginAsAdmin(FileBrowserPage.class, DefaultFuncTestData.getProject1(), DefaultFuncTestData.getProject1Repository1());
        assertUIMatches("file-browser");
    }

    @Test
    public void testSourceViewPage() {
        String fileName = "file1.txt";
        loginAsAdmin(FileSourcePage.class, PROJECT_KEY, REPO_SLUG, fileName);
        assertUIMatches("file-content-source-view");
    }

    @Test
    public void testDiffViewPage() {
        String pathAndFileName = "ChangeSet/file1.txt";
        String revision = "b4d378388c2dc3840ff5dcf64b4ac0f1ffc17cb9";
        FileLayoutPage.RevisionSpecifier revisionSpecifier = new FileLayoutPage.RevisionSpecifier(null, revision);
        loginAsAdmin(FileUnifiedDiffPage.class, PROJECT_KEY, REPO_SLUG, pathAndFileName, revisionSpecifier);
        assertUIMatches("file-content-diff-view");
    }

    @Ignore
    @Test
    public void testCommitPage() throws Exception {
        // The MultipleUIMatcher collects all errors and throws them all at the end rather than failing the test
        // at the first exception it hits.
        MultipleUIMatcher errors = new MultipleUIMatcher();

        String revision = "67ea12f085107dcecdf15bb00c6f60c66b92d620";
        // Login as an admin user and navigate to the commit page
        ChangesetPage page = loginAsAdmin(ChangesetPage.class, PROJECT_KEY, REPO_SLUG, revision);
        // at this commit, the first selected file is a deleted file
        errors.assertUIMatches("repository-changeset-file-deleted");

        // resets the visual comparer so we get fresh screenshots to compare
        resetVisualComparer();
        // click on a file that's been moved
        page.clickFileAndWaitUntilLoaded("ChangeSet/bootstrap/examples/README.md");
        errors.assertUIMatches("repository-changeset-file-moved");

        resetVisualComparer();
        // click on a file that's been modified
        page.clickFileAndWaitUntilLoaded("ChangeSet/bootstrap/js/bootstrap-alerts.js");
        errors.assertUIMatches("repository-changeset-file-modified");

        resetVisualComparer();
        // click on a file that's been added
        page.clickFileAndWaitUntilLoaded("ChangeSet/bootstrap/file3.txt");
        errors.assertUIMatches("repository-changeset-file-added");

        resetVisualComparer();
        // click on a file that's been renamed
        page.clickFileAndWaitUntilLoaded("ChangeSet/bootstrap/Makefile.bak");
        errors.assertUIMatches("repository-changeset-file-renamed");

        resetVisualComparer();
        // click on the button that collapses the file tree
        page.collapseFileTreeContainer();
        errors.assertUIMatches("repository-changeset-tree-collapsed");

        // Throws all the errors if there are any
        errors.assertNoError();
    }

    @Ignore
    @Test
    public void testPullRequestPage() throws Exception {
        // The MultipleUIMatcher collects all errors and throws them all at the end rather than failing the test
        // at the first exception it hits.
        MultipleUIMatcher errors = new MultipleUIMatcher();

        // Login as an admin user and navigate to the pull request overview page for the pull request we created earlier
        PullRequestOverviewPage page = loginAsAdmin(PullRequestOverviewPage.class, pullRequestBuilder.getTargetProject(), pullRequestBuilder.getTargetRepo(), pullRequest.getId());
        // check that the pull request 'overview' tab's screenshots match
        errors.assertUIMatches("pull-request-overview-tab");

        resetVisualComparer();
        // click on the 'diff' tab
        PullRequestDiffPage diffPage = page.switchToDiff();
        // check that the 'diff' tab's screenshots match
        errors.assertUIMatches("pull-request-diff-tab");

        resetVisualComparer();
        // open an inline comment form and populate it with some text
        DiffFileContent.Line line = diffPage.getFileContent().getDiffLines().get(0);
        String inlineComment = "Inline comment";
        DiffCommentForm diffCommentForm = diffPage.openNewComment(line, inlineComment);
        // the comment form has randomised tips, so we replace it with something consistent for visual testing
        replaceCommentTipText();
        // check that the diff view with comment form screenshots match
        errors.assertUIMatches("pull-request-diff-comment-form");

        // try to preview it
        resetVisualComparer();
        diffCommentForm.togglePreview();
        // check that the diff view with comment preview screenshots match
        errors.assertUIMatches("pull-request-diff-comment-preview");

        resetVisualComparer();
        // click on the 'commits' tab
        page.switchToCommits();
        // check that the 'commits' tab's screenshots match
        errors.assertUIMatches("pull-request-commits-tab");

        // Throws all the errors if there are any
        errors.assertNoError();
    }

    // Replace the Comment form "Tip" text because it's randomized and causes flaky tests, but we don't want to ignore
    // the whole thing
    protected void replaceCommentTipText() {
        // run our own JS to do the text replacement because the replaceUIHtml method only takes in IDs, not classes or css selectors
        client.execute("jQuery('.comment-form-container .tip').html('<span class=\"tip-label\">Tip: </span>Comment form tip')");
    }
}