package org.mockserver.serialization.serializers.body;

import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.Test;
import org.mockserver.matchers.MatchType;
import org.mockserver.model.JsonBody;
import org.mockserver.model.MediaType;
import org.mockserver.serialization.ObjectMapperFactory;
import org.mockserver.serialization.model.JsonBodyDTO;

import java.nio.charset.StandardCharsets;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.mockserver.character.Character.NEW_LINE;
import static org.mockserver.model.JsonBody.json;

public class JsonBodyDTOSerializerTest {

    @Test
    public void shouldSerializeJsonBodyDTO() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{fieldOne: \"valueOne\", \"fieldTwo\": \"valueTwo\"}"))),
            is("{\"fieldOne\":\"valueOne\",\"fieldTwo\":\"valueTwo\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOAsObjectPrettyPrintedWithoutDefaultFields() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper(true, false).writeValueAsString(new JsonBodyDTO(json(new JsonBodySerializerTest.TestObject()))),
            equalTo("{" + NEW_LINE +
                "  \"fieldOne\" : \"valueOne\"," + NEW_LINE +
                "  \"fieldTwo\" : \"valueTwo\"" + NEW_LINE +
                "}"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOAsObjectPrettyPrintedWithDefaultFields() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper(true, true).writeValueAsString(new JsonBodyDTO(json(new JsonBodySerializerTest.TestObject()))),
            equalTo("{" + NEW_LINE +
                "  \"type\" : \"JSON\"," + NEW_LINE +
                "  \"json\" : {" + NEW_LINE +
                "    \"fieldOne\" : \"valueOne\"," + NEW_LINE +
                "    \"fieldTwo\" : \"valueTwo\"" + NEW_LINE +
                "  }," + NEW_LINE +
                "  \"rawBytes\" : \"eyJmaWVsZE9uZSI6InZhbHVlT25lIiwiZmllbGRUd28iOiJ2YWx1ZVR3byJ9\"" + NEW_LINE +
                "}"));
    }

    @Test
    public void shouldSerializeJsonBodyWithDefaultMatchType() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{fieldOne: \"valueOne\", \"fieldTwo\": \"valueTwo\"}", MatchType.ONLY_MATCHING_FIELDS))),
            is("{\"fieldOne\":\"valueOne\",\"fieldTwo\":\"valueTwo\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyWithNoneDefaultMatchType() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{fieldOne: \"valueOne\", \"fieldTwo\": \"valueTwo\"}", MatchType.STRICT))),
            is("{\"type\":\"JSON\",\"json\":{\"fieldOne\":\"valueOne\",\"fieldTwo\":\"valueTwo\"},\"rawBytes\":\"e2ZpZWxkT25lOiAidmFsdWVPbmUiLCAiZmllbGRUd28iOiAidmFsdWVUd28ifQ==\",\"matchType\":\"STRICT\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyWithNoneDefaultMatchTypeAndCharset() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{fieldOne: \"valueOne\", \"fieldTwo\": \"valueTwo\"}", null, MediaType.create("application", "json").withCharset(StandardCharsets.UTF_16), MatchType.STRICT))),
            is("{\"contentType\":\"application/json; charset=utf-16\",\"type\":\"JSON\",\"json\":{\"fieldOne\":\"valueOne\",\"fieldTwo\":\"valueTwo\"},\"rawBytes\":\"/v8AewBmAGkAZQBsAGQATwBuAGUAOgAgACIAdgBhAGwAdQBlAE8AbgBlACIALAAgACIAZgBpAGUAbABkAFQAdwBvACIAOgAgACIAdgBhAGwAdQBlAFQAdwBvACIAfQ==\",\"matchType\":\"STRICT\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyWithDefaultMatchTypeAndContentType() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{fieldOne: \"valueOne\", \"fieldTwo\": \"valueTwo\"}", null, MediaType.JSON_UTF_8, MatchType.STRICT))),
            is("{\"type\":\"JSON\",\"json\":{\"fieldOne\":\"valueOne\",\"fieldTwo\":\"valueTwo\"},\"rawBytes\":\"e2ZpZWxkT25lOiAidmFsdWVPbmUiLCAiZmllbGRUd28iOiAidmFsdWVUd28ifQ==\",\"matchType\":\"STRICT\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyWithNoneDefaultMatchTypeAndContentType() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{fieldOne: \"valueOne\", \"fieldTwo\": \"valueTwo\"}", null, MediaType.parse("application/json; charset=utf-16"), MatchType.STRICT))),
            is("{\"contentType\":\"application/json; charset=utf-16\",\"type\":\"JSON\",\"json\":{\"fieldOne\":\"valueOne\",\"fieldTwo\":\"valueTwo\"},\"rawBytes\":\"/v8AewBmAGkAZQBsAGQATwBuAGUAOgAgACIAdgBhAGwAdQBlAE8AbgBlACIALAAgACIAZgBpAGUAbABkAFQAdwBvACIAOgAgACIAdgBhAGwAdQBlAFQAdwBvACIAfQ==\",\"matchType\":\"STRICT\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOWithNot() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{fieldOne: \"valueOne\", \"fieldTwo\": \"valueTwo\"}", MatchType.STRICT), true)),
            is("{\"not\":true,\"type\":\"JSON\",\"json\":{\"fieldOne\":\"valueOne\",\"fieldTwo\":\"valueTwo\"},\"rawBytes\":\"e2ZpZWxkT25lOiAidmFsdWVPbmUiLCAiZmllbGRUd28iOiAidmFsdWVUd28ifQ==\",\"matchType\":\"STRICT\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOWithOptional() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{fieldOne: \"valueOne\", \"fieldTwo\": \"valueTwo\"}", MatchType.STRICT)).withOptional(true)),
            is("{\"optional\":true,\"type\":\"JSON\",\"json\":{\"fieldOne\":\"valueOne\",\"fieldTwo\":\"valueTwo\"},\"rawBytes\":\"e2ZpZWxkT25lOiAidmFsdWVPbmUiLCAiZmllbGRUd28iOiAidmFsdWVUd28ifQ==\",\"matchType\":\"STRICT\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOWithMatchNumbersAsStrings() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{\"value\":1}", null, null, MatchType.ONLY_MATCHING_FIELDS, true))),
            is("{\"type\":\"JSON\",\"json\":{\"value\":1},\"rawBytes\":\"eyJ2YWx1ZSI6MX0=\",\"matchNumbersAsStrings\":true}"));
    }

    @Test
    public void shouldNotSerializeJsonBodyDTOWithMatchNumbersAsStringsFalse() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("{\"value\":1}", null, null, MatchType.ONLY_MATCHING_FIELDS, false))),
            is("{\"value\":1}"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOWithStringPrimitive() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("\"test\""))),
            is("\"test\""));
    }

    @Test
    public void shouldSerializeJsonBodyDTOWithStringPrimitiveAndNonDefaultFields() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("\"test\"", MatchType.STRICT))),
            is("{\"type\":\"JSON\",\"json\":\"test\",\"rawBytes\":\"InRlc3Qi\",\"matchType\":\"STRICT\"}"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOWithNumberPrimitive() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("42"))),
            is("42"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOWithBooleanPrimitive() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("true"))),
            is("true"));
    }

    @Test
    public void shouldSerializeJsonBodyDTOWithNullPrimitive() throws JsonProcessingException {
        assertThat(ObjectMapperFactory.createObjectMapper().writeValueAsString(new JsonBodyDTO(new JsonBody("null"))),
            is("null"));
    }
}
