package comments;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import comments.main.Application;
import comments.model.Comment;
import io.restassured.RestAssured;
import io.restassured.http.Header;
import io.restassured.response.Response;
import io.restassured.response.ValidatableResponse;
import net.minidev.json.JSONObject;
import org.junit.*;
import org.junit.jupiter.api.Assertions;

import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

public class CommentsTest {

    private static final String BASE_URI_COMMENTS = "http://localhost:8080/api/comments";

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private Integer responseStatusCode;
    private String responseBody;

    @BeforeClass
    public static void setup() {
        Application.main(new String[]{});
    }

    @Before
    public void setupTest() {
        responseStatusCode = null;
        responseBody = null;
    }

    @After
    public void tearDown() {
        Assert.assertNotNull(responseStatusCode);
        if (responseStatusCode == 204) Assert.assertEquals("", responseBody);
        else Assert.assertNotEquals("", responseBody);
    }

    public <T> T assertOkResponseOfType(final TypeReference<T> type) {
        Assert.assertEquals(2, responseStatusCode / 100);
        try {
            return OBJECT_MAPPER.readValue(responseBody, type);
        } catch (final Throwable e) {
            fail(e.getMessage());
            return null;
        }
    }

    @Test
    public void getAllComments() {
        ValidatableResponse response = RestAssured.given().log().all()
                .when()
                .get(BASE_URI_COMMENTS)
                .then().log().all();

        responseStatusCode = response.extract().statusCode();
        responseBody = response.extract().body().asPrettyString();

        assertOkResponseOfType(new TypeReference<List<Comment>>() {});
    }

    @Test
    public void getOneComment() {
        ValidatableResponse response = RestAssured.given().log().all()
                .when()
                .get(BASE_URI_COMMENTS + "/c1")
                .then().log().all();

        responseStatusCode = response.extract().statusCode();
        responseBody = response.extract().body().asPrettyString();

        assertOkResponseOfType(new TypeReference<Comment>() {});
    }

    @Test
    public void getOneNonExistingComment() {
        ValidatableResponse response = RestAssured.given().log().all()
                .when()
                .get(BASE_URI_COMMENTS + "/fsfasdf")
                .then().log().all();

        responseStatusCode = response.extract().statusCode();
        responseBody = response.extract().body().asPrettyString();

        Assert.assertEquals(404, responseStatusCode.intValue());
    }

    @Test
    public void putComment() {

        JSONObject requestParam = new JSONObject();
        requestParam.put("id", "c1");
        requestParam.put("userName", "Prueba");
        requestParam.put("text", "Hello my name is John Doe");
        requestParam.put("date", "2015-01-16T20:44:53.950");
        requestParam.put("type", "Complain");

        Header acceptJson = new Header("content-type", "application/json");

        ValidatableResponse response = RestAssured.given().log().all()
                .header(acceptJson)
                .body(requestParam.toJSONString())
                .when()
                .put(BASE_URI_COMMENTS)
                .then().log().all();

        responseStatusCode = response.extract().statusCode();
        responseBody = response.extract().body().asPrettyString();

        final JSONObject expectedResponse = new JSONObject();
        expectedResponse.put("date", "2017-06-16T20:44:53.950");
        expectedResponse.put("id", "c1");
        expectedResponse.put("text", "Hello my name is John Doe");
        expectedResponse.put("userName", "johnSmith");
        expectedResponse.put("type", "Review");

        final JSONObject c = assertOkResponseOfType(new TypeReference<JSONObject>() {});
        assertEquals(expectedResponse, c);
    }

    @Test
    public void postComment() {

        JSONObject requestParam = new JSONObject();
        requestParam.put("id", "c1");
        requestParam.put("userName", "Prueba");
        requestParam.put("text", "Hello my name is John Doe");
        requestParam.put("date", "2015-01-16T20:44:53.950");

        Header acceptJson = new Header("content-type", "application/json");

        ValidatableResponse response = RestAssured.given().log().all()
                .header(acceptJson)
                .body(requestParam.toJSONString())
                .when()
                .post(BASE_URI_COMMENTS)
                .then().log().all();

        responseStatusCode = response.extract().statusCode();
        responseBody = response.extract().body().asPrettyString();

        final JSONObject c = assertOkResponseOfType(new TypeReference<JSONObject>() {});
        Assert.assertEquals(201, responseStatusCode.intValue());
        requestParam.put("type", null);
        requestParam.put("id", "c12");
        Assert.assertEquals(requestParam, c);
    }

    @Test
    public void deleteOneComment() {
        ValidatableResponse response = RestAssured.given().log().all()
                .when()
                .delete(BASE_URI_COMMENTS + "/c2")
                .then().log().all();

        responseStatusCode = response.extract().statusCode();
        responseBody = response.extract().body().asPrettyString();

        Assert.assertEquals(204, responseStatusCode.intValue());
        Assert.assertEquals("", responseBody);
    }


    @Test
    public void postAndGetComment() {

        JSONObject requestParam = new JSONObject();
        requestParam.put("id", "c1");
        requestParam.put("userName", "Prueba");
        requestParam.put("text", "Hello my name is John Doe");
        requestParam.put("date", "2015-01-16T20:44:53.950");

        Header acceptJson = new Header("content-type", "application/json");

        Response previousResponse = RestAssured.given().log().all()
                .header(acceptJson)
                .body(requestParam.toJSONString())
                .when()
                .post(BASE_URI_COMMENTS);
        previousResponse.then().log().all();

        ValidatableResponse response = RestAssured.given().log().all()
                .when()
                .get(BASE_URI_COMMENTS + "/" + previousResponse.getBody().path("id"))
                .then().log().all();

        responseStatusCode = response.extract().statusCode();
        responseBody = response.extract().body().asPrettyString();

        final JSONObject c = assertOkResponseOfType(new TypeReference<JSONObject>() {});
        requestParam.put("id", "c11");
        requestParam.put("type", null);
        assertEquals(requestParam, c);
    }

}
