package io.vavr.jackson.issues;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.vavr.control.Option;
import io.vavr.jackson.datatype.BaseTest;
import io.vavr.jackson.datatype.VavrModule;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.time.YearMonth;
import java.util.Optional;

public class Issue141Test extends BaseTest {

    static class MyJavaOptionalClass {
        @JsonProperty("operatingMonth")
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MM-yyyy")
        Optional<YearMonth> operatingMonth;
    }

    @Test
    public void itShouldSerializeJavaOptionalYearMonthAsString() throws IOException {
        // Given an instance with java.util.Optional
        MyJavaOptionalClass obj = new MyJavaOptionalClass();
        obj.operatingMonth = Optional.of(YearMonth.of(2019, 12));

        // When serializing the instance using object mapper
        // with Java Time Module and JDK8 Module
        ObjectMapper objectMapper = mapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.registerModule(new Jdk8Module());
        String json = objectMapper.writeValueAsString(obj);

        // Then serialization is successful
        Assertions.assertEquals("{\"operatingMonth\":\"12-2019\"}", json);

        // And deserialization is successful
        MyJavaOptionalClass obj2 = objectMapper.readValue(json, MyJavaOptionalClass.class);
        Assertions.assertEquals(Optional.of(YearMonth.of(2019, 12)), obj2.operatingMonth);
    }

    static class MyVavrOptionalClassWithoutFormat {
        @JsonProperty("operatingMonth")
        Option<YearMonth> operatingMonth;
    }

    @Test
    public void itShouldSerializeVavrOptionYearMonthAsStringWithoutJsonFormat() throws IOException {
        // Given an instance with io.vavr.control.Option
        MyVavrOptionalClassWithoutFormat obj = new MyVavrOptionalClassWithoutFormat();
        obj.operatingMonth = Option.of(YearMonth.of(2019, 12));

        // When serializing the instance using object mapper
        // with Java Time Module and VAVR Module
        ObjectMapper objectMapper = mapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.registerModule(new VavrModule());
        String json = objectMapper.writeValueAsString(obj);

        // Then serialization is successful
        Assertions.assertEquals("{\"operatingMonth\":[2019,12]}", json);
        MyVavrOptionalClassWithoutFormat obj2 = objectMapper.readValue(json, MyVavrOptionalClassWithoutFormat.class);

        // And deserialization is successful
        Assertions.assertEquals(Option.of(YearMonth.of(2019, 12)), obj2.operatingMonth);
    }

    static class MyVavrOptionClassWithFormat {
        @JsonProperty("operatingMonth")
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MM-yyyy")
        Option<YearMonth> operatingMonth;
    }

    @Test
    public void itShouldSerializeVavrOptionYearMonthAsString() throws IOException {
        // Given an instance with io.vavr.control.Option
        MyVavrOptionClassWithFormat obj = new MyVavrOptionClassWithFormat();
        obj.operatingMonth = Option.of(YearMonth.of(2019, 12));

        // When serializing the instance using object mapper
        // with Java Time Module and VAVR Module
        ObjectMapper objectMapper = mapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.registerModule(new VavrModule());
        String json = objectMapper.writeValueAsString(obj);

        // Then serialization is failed
        Assertions.assertEquals("{\"operatingMonth\":\"12-2019\"}", json);
        MyVavrOptionClassWithFormat obj2 = objectMapper.readValue(json, MyVavrOptionClassWithFormat.class);

        // And deserialization is failed
        Assertions.assertEquals(Option.of(YearMonth.of(2019, 12)), obj2.operatingMonth);
    }
}