package it.com.atlassian.stash.rest.admin;

import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.bitbucket.test.MaintenanceModeHelper;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.http.ContentType;
import it.com.atlassian.stash.BaseRestTest;
import org.junit.Test;

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;

import static javax.ws.rs.core.Response.Status.*;
import static org.hamcrest.CoreMatchers.equalTo;

public class MaintenanceControllerRestTest extends BaseRestTest {

    private static final String USER_REGULAR = DefaultFuncTestData.getRegularUser();
    private static final String USER_ADMIN = DefaultFuncTestData.getAdminUser();
    private static final String USER_PASSWORD_REGULAR = DefaultFuncTestData.getRegularUserPassword();
    private static final String USER_PASSWORD_ADMIN = DefaultFuncTestData.getAdminPassword();

    /**
     * Verifies that plugin-provided filters are bypassed while the system is locked for maintenance.
     * <p>
     * Plugin filters frequently end up blocked on the database latch during maintenance mode, often triggering
     * support requests because:
     * <ul>
     *     <li>the system appears hung during backup</li>
     *     <li>the backup client cannot {@code POST} progress, resulting in it timing out</li>
     * </ul>
     * To try and avoid issues like these, starting from 3.2 <i>all</i> plugin-provided filters are bypassed
     * while the system is locked.
     *
     * @throws Exception should not be thrown
     */
    @Test
    public void testPluginFiltersShouldBeBypassedWhileLocked() throws Exception {
        //First, verify that, while the system is not locked, the JohnsonCheckFilter is invoked and intercepts
        //requests to the ApplicationStatusServlet
        String checkUrl = DefaultFuncTestData.getBaseURL() + "/status?johnsonCheck=true";
        RestAssured.expect()
                .statusCode(HttpServletResponse.SC_OK)
                .contentType(ContentType.TEXT) //Interception replaces the content type...
                .body(equalTo("Intercepted!")) //...and the body
                .when().get(checkUrl);

        String unlockToken = MaintenanceModeHelper.lockStash();
        try {
            MaintenanceModeHelper.assertStashLocked(true);

            //Then, once the system is locked, verify that the JohnsonCheckFilter is bypassed, even though the
            //check is still requested, and the ApplicationStatusServlet is called (and indicates that the
            //system is in maintenance mode)
            RestAssured.expect()
                    .statusCode(HttpServletResponse.SC_OK)
                    .contentType(ContentType.JSON)
                    .body("state", equalTo("MAINTENANCE"))
                    .when().get(checkUrl);
        } finally {
            MaintenanceModeHelper.unlockStash(unlockToken);
        }
        MaintenanceModeHelper.assertStashLocked(false);
    }

    @Test
    public void testShouldLockAndUnlockTheSystem() throws Exception {
        String unlockToken = MaintenanceModeHelper.lockStash();
        try {
            MaintenanceModeHelper.assertStashLocked(true);
        } finally {
            MaintenanceModeHelper.unlockStash(unlockToken);
        }
        MaintenanceModeHelper.assertStashLocked(false);
    }

    @Test
    public void testShouldNotUnlockWithIncorrectToken() throws Exception {
        String unlockToken = MaintenanceModeHelper.lockStash();
        try {
            MaintenanceModeHelper.unlockStashIgnoreStatus("deadbeef");
            MaintenanceModeHelper.assertStashLocked(true);
        } finally {
            MaintenanceModeHelper.unlockStash(unlockToken);
        }
    }

    @Test
    public void testShouldNotLockWithANonSysadminUser() throws Exception {
        RestAssured.given()
            .auth().preemptive().basic(USER_REGULAR, USER_PASSWORD_REGULAR)
            .expect()
            .statusCode(UNAUTHORIZED.getStatusCode())
            .when()
            .post(MaintenanceModeHelper.LOCK_URL);
    }

    @Test
    public void testShouldNotLockForXsrfPostWithFormUrlencoded() throws Exception {
        RestAssured.given()
            .header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED)
            .auth().preemptive().basic(USER_ADMIN, USER_PASSWORD_ADMIN)
            .expect()
            .statusCode(UNSUPPORTED_MEDIA_TYPE.getStatusCode())
            .when()
            .post(MaintenanceModeHelper.LOCK_URL);
    }

    @Test
    public void testShouldNotLockForXsrfPostWithMultipartFormData() throws Exception {
        RestAssured.given()
            .multiPart("document", "{}", "application/json")
            .multiPart("image", "", "image/jpeg")
            .formParam("name", "value")
            .log().all()
            .auth().preemptive().basic(USER_ADMIN, USER_PASSWORD_ADMIN)
            .expect()
            .statusCode(UNSUPPORTED_MEDIA_TYPE.getStatusCode())
            .when()
            .post(MaintenanceModeHelper.LOCK_URL);
    }

    @Test
    public void testShouldNotLockForXsrfPostWithTextPlain() throws Exception {
        RestAssured.given()
            .header("Content-Type", MediaType.TEXT_PLAIN)
            .auth().preemptive().basic(USER_ADMIN, USER_PASSWORD_ADMIN)
            .expect()
            .statusCode(UNSUPPORTED_MEDIA_TYPE.getStatusCode())
            .when()
            .post(MaintenanceModeHelper.LOCK_URL);
    }

    @Test
    public void testShouldUnlockWithANonSysadminUser() throws Exception {
        String unlockToken = MaintenanceModeHelper.lockStash();
        try {
            MaintenanceModeHelper.unlockWithUserExpectingStatus(unlockToken, USER_REGULAR, USER_PASSWORD_REGULAR, OK);
        } finally {
            MaintenanceModeHelper.unlockStashIgnoreStatus(unlockToken);
        }
    }
}