package it.com.atlassian.stash.defaultreviewers.helpers;

import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.bitbucket.test.PullRequestTestHelper;
import com.atlassian.pageobjects.TestedProductFactory;
import com.atlassian.webdriver.bitbucket.BitbucketTestedProduct;
import com.atlassian.webdriver.bitbucket.element.PullRequestDetails;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.Response;
import it.com.atlassian.stash.defaultreviewers.branch.BranchTypes;
import it.com.atlassian.stash.defaultreviewers.page.DefaultReviewersPage;
import org.apache.commons.lang3.StringUtils;

import javax.ws.rs.core.MediaType;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static com.atlassian.bitbucket.test.DefaultFuncTestData.*;
import static com.atlassian.bitbucket.test.PullRequestTestHelper.getUrl;
import static java.util.stream.Collectors.toList;
import static javax.ws.rs.core.Response.Status.OK;
import static org.junit.Assert.assertEquals;

public class DefaultReviewerFuncTestHelper {
    private static final BitbucketTestedProduct BITBUCKET = TestedProductFactory.create(BitbucketTestedProduct.class);



    public static void assertNoRulesExist(DefaultReviewersPage defaultReviewersPage) {
        assertEquals("There should be no default reviewer rules", 0, defaultReviewersPage.getRows().size());
    }

    public static void assertRuleExists(DefaultReviewersPage defaultReviewersPage,
                                        BranchTypes sourceType, BranchTypes targetType,
                                        String sourceValue, String targetValue,
                                        List<String> reviewers, int requiredApprovals,
                                        int matchingRows) {
        assertEquals("There should be exactly " + String.valueOf(matchingRows) + " rows with: \n" +
                        "Source branch type: " + sourceType + "\n" +
                        "Target branch type: " + targetType + "\n" +
                        "Reviewers: " + StringUtils.join(reviewers, ", ") + "\n" +
                        "Required approvals: " + String.valueOf(requiredApprovals),
                matchingRows,
                defaultReviewersPage
                        .getRows()
                        .stream()
                        .filter(row -> row.getSourceBranchType().equals(sourceType.toString()))
                        .filter(row -> row.getSourceBranchValue().equals(sourceValue))
                        .filter(row -> row.getTargetBranchType().equals(targetType.toString()))
                        .filter(row -> row.getTargetBranchValue().equals(targetValue))
                        .filter(row -> row.getReviewers().equals(reviewers))
                        .filter(row -> row.getRequiredApprovals() == requiredApprovals)
                        .count());
    }

    public static void clearBrowserCookies() {
        BITBUCKET.getTester().getDriver().manage().deleteAllCookies();
    }


    public static void declineAllOpenPullRequests() {
        getOpenPullRequests()
                .forEach(DefaultReviewerFuncTestHelper::declinePullRequest);
    }

    private static void declinePullRequest(long pullRequestId) {
        declinePullRequest(pullRequestId, getAdminUser(), getAdminPassword());
    }

    private static void declinePullRequest(long pullRequestId, String declinerUsername, String declinerPassword) {
        if (pullRequestId == -1L) {
            return;
        }
        PullRequestTestHelper pullRequest = PullRequestTestHelper.get(getAdminUser(), getAdminPassword(),
                getProject1(), getProject1Repository1(), pullRequestId);

        PullRequestTestHelper.declinePullRequest(
                declinerUsername, declinerPassword,
                getProject1(), getProject1Repository1(),
                pullRequestId, pullRequest.getVersion()
        );
    }

    public static void deleteAllDefaultReviewerRules() {
        getDefaultReviewerRules()
                .forEach(DefaultReviewerFuncTestHelper::deleteDefaultReviewerRule);
    }

    private static void deleteDefaultReviewerRule(Integer id) {
        RestAssured.expect().statusCode(OK.getStatusCode())
                .expect()
                .statusCode(OK.getStatusCode())
                .log().ifError()
                .given()
                .auth().preemptive().basic(getAdminUser(), getAdminPassword())
                .delete(getRestURL("default-reviewers", "latest") +
                        getRepositoryPath(getProject1(), getProject1Repository1()) +
                        "/condition/" + String.valueOf(id));
    }


    private static List<Integer> getDefaultReviewerRules() {
        Response response = RestAssured.expect().statusCode(OK.getStatusCode())
                .expect()
                .statusCode(OK.getStatusCode())
                .log().ifError()
                .given()
                .auth().preemptive().basic(getAdminUser(), getAdminPassword())
                .contentType(MediaType.APPLICATION_JSON)
                .get(getRestURL("default-reviewers", "latest") +
                        getRepositoryPath(getProject1(), getProject1Repository1()) +
                        "/conditions");
        List<Map<String, Object>> values = response.jsonPath().get();
        return values
                .stream()
                .map(rule -> (Integer) rule.get("id"))
                .collect(toList());
    }

    private static List<Integer> getOpenPullRequests() {
        Response response = RestAssured.expect().statusCode(OK.getStatusCode())
                .expect()
                .statusCode(OK.getStatusCode())
                .log().ifError()
                .given()
                .auth().preemptive().basic(getAdminUser(), getAdminPassword())
                .contentType(MediaType.APPLICATION_JSON)
                .get(getUrl(getProject1(), getProject1Repository1()));
        List<Map<String, Object>> values = response
                .jsonPath()
                .get("values");
        return values
                .stream()
                .map(prInfo -> (Integer) prInfo.get("id"))
                .collect(toList());
    }

    public static Stream<String> getReviewers(PullRequestDetails details) {
        return StreamSupport.stream(details
                .getReviewerUsernames()
                .byDefaultTimeout()
                .spliterator(), false);
    }

    public static void noFeatureDiscovery(String username, String password) {
        RestAssured
                .given().auth().preemptive().basic(username, password)
                .formParam("json", "{\"isAllDismissed\": true}")
                .header(DefaultFuncTestData.HDR_XSRF_NO_CHECK)
                .expect().statusCode(javax.ws.rs.core.Response.Status.OK.getStatusCode())
                .when().put(DefaultFuncTestData.getRestURL("chaperone", "1") + "/chaperone/default-reviewers");
    }
}
