package com.atlassian.bitbucket.test.rest;

import com.google.common.base.MoreObjects;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.path.json.JsonPath;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;

public abstract class AbstractRestHelper {

    protected final RestAuthentication defaultAuthentication;

    protected AbstractRestHelper(@Nullable RestAuthentication defaultAuthentication) {
        this.defaultAuthentication = defaultAuthentication == null ?
                RestAuthentication.anonymous() :
                defaultAuthentication;
    }

    public RequestSpecification request() {
        return authenticatedRequest(defaultAuthentication);
    }

    protected JsonPath delete(@Nonnull String url, @Nonnull AbstractRestRequest request) {
        return buildRequest(request)
                .delete(url)
                .jsonPath();
    }

    protected JsonPath get(@Nonnull String url, @Nonnull AbstractRestRequest request) {
        return buildRequest(request)
                .get(url)
                .jsonPath();
    }

    protected JsonPath post(@Nonnull String url, @Nonnull AbstractRestRequest request) {
        return buildRequest(request, true) //POST requests can include a body
                .post(url)
                .jsonPath();
    }

    protected JsonPath put(@Nonnull String url, @Nonnull AbstractRestRequest request) {
        return buildRequest(request, true) //PUT requests can include a body
                .put(url)
                .jsonPath();
    }

    private static RequestSpecification authenticatedRequest(RestAuthentication authentication) {
        RequestSpecification builder = jsonRequest();
        if (authentication.isAuthenticated()) {
            builder.auth().preemptive().basic(authentication.getUsername(), authentication.getPassword());
        } else {
            builder.auth().none();
        }

        return builder;
    }

    private static RequestSpecification jsonRequest() {
        return RestAssured.given().contentType("application/json");
    }

    private RequestSpecification authenticatedRequest(AbstractRestRequest request) {
        return authenticatedRequest(MoreObjects.firstNonNull(request.getAuthentication(), defaultAuthentication));
    }

    private ResponseSpecification buildRequest(AbstractRestRequest request) {
        return buildRequest(request, false);
    }

    private ResponseSpecification buildRequest(AbstractRestRequest request, boolean withBody) {
        RequestSpecification builder = authenticatedRequest(request);
        if (withBody && request instanceof HasBody) {
            builder.body(((HasBody) request).getBody());
        }

        return builder.expect()
                .spec(request.getResponseSpecification())
                .log().ifStatusCodeMatches(not(equalTo(request.getStatus().getStatusCode())))
                .log().ifValidationFails();
    }
}
