/*
 * Copyright 2019 Immutables Authors and Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.immutables.criteria.mongo.bson4jackson;

import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.util.BufferRecycler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.mongodb.client.model.Filters;
import org.bson.BsonBinaryReader;
import org.bson.BsonBinaryWriter;
import org.bson.BsonDocument;
import org.bson.BsonRegularExpression;
import org.bson.BsonWriter;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import org.bson.io.BasicOutputBuffer;
import org.bson.types.ObjectId;
import org.immutables.value.Value;
import org.junit.Test;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.time.LocalDate;
import java.util.function.Consumer;
import java.util.regex.Pattern;

import static org.immutables.check.Checkers.check;

public class JacksonCodecsTest {

  /**
   * Test that regular expression is correctly encoded
   */
  @Test
  public void regex() {
    final ObjectMapper mapper = JacksonCodecs.register(new ObjectMapper());
    final CodecRegistry registry = JacksonCodecs.registryFromMapper(mapper);

    Consumer<Bson> validate = bson -> {
      BsonDocument doc = bson.toBsonDocument(BsonDocument.class, registry);
      check(doc.get("a")).is(new BsonRegularExpression("a.*b"));
    };

    validate.accept(Filters.eq("a", Pattern.compile("a.*b")));
    validate.accept(Filters.regex("a", Pattern.compile("a.*b")));
    validate.accept(Filters.regex("a", "a.*b"));
  }

  @Test
  public void encodeDecode() throws IOException {
    BsonModel model = ImmutableBsonModel.builder().utilDate(new java.util.Date())
            .localDate(LocalDate.now())
            .pattern(Pattern.compile("a.*b"))
            .objectId(ObjectId.get())
            .build();

    final ObjectMapper mapper = JacksonCodecs.register(new ObjectMapper());
    final CodecRegistry registry = JacksonCodecs.registryFromMapper(mapper);

    BasicOutputBuffer buffer = new BasicOutputBuffer();
    BsonWriter writer = new BsonBinaryWriter(buffer);

    registry.get(BsonModel.class).encode(writer, model, EncoderContext.builder().build());

    BsonBinaryReader reader = new BsonBinaryReader(ByteBuffer.wrap(buffer.toByteArray()));
    IOContext ioContext = new IOContext(new BufferRecycler(), null, false);
    BsonParser parser = new BsonParser(ioContext, 0, reader);

    // read
    BsonModel model2 = mapper.readValue(parser, BsonModel.class);

    check(model2.localDate()).is(model.localDate());
    check(model2.utilDate()).is(model.utilDate());
    check(model2.objectId()).is(model.objectId());
  }

  @Value.Immutable
  @JsonSerialize(as = ImmutableBsonModel.class)
  @JsonDeserialize(as = ImmutableBsonModel.class)
  interface BsonModel {
      java.util.Date utilDate();
      LocalDate localDate();
      Pattern pattern();
      ObjectId objectId();
  }

}
@@ -940,32 +940,89 @@
 .JsonSerialize;%0A
+import com.fasterxml.jackson.datatype.guava.GuavaModule;%0A
 import com.mongo
@@ -1549,16 +1549,38 @@
 alDate;%0A
+import java.util.Map;%0A
 import j
@@ -2611,24 +2611,60 @@
 ctId.get())%0A
+            .putMap(%22key1%22, %22val1%22)%0A
             
@@ -2740,32 +2740,79 @@
  ObjectMapper())
+%0A            .registerModule(new GuavaModule())
 ;%0A    final Code
@@ -3538,24 +3538,112 @@
 bjectId());%0A
+    check(model2.map().keySet()).isOf(%22key1%22);%0A    check(model2.map()).is(model.map());%0A
   %7D%0A%0A  @Valu
@@ -3890,14 +3890,47 @@
 ctId();%0A
+      Map%3CString, String%3E map();%0A
   %7D%0A%0A%7D

Add bson test for Map attribute