// Path: RosettaCore/src/main/java/com/hubspot/rosetta/RosettaBinder.java
// public enum RosettaBinder {
//   INSTANCE;
// 
//   public interface Callback {
//     void bind(String key, Object value);
//   }
// 
//   public void bind(String prefix, JsonNode node, Callback callback) {
//     for (Iterator<Entry<String, JsonNode>> iterator = node.fields(); iterator.hasNext(); ) {
//       Entry<String, JsonNode> field = iterator.next();
//       String key = prefix.isEmpty() ? field.getKey() : prefix + "." + field.getKey();
//       JsonNode value = field.getValue();
// 
//       if (value.isObject()) {
//         bind(key, value, callback);
//       } else if (value.isArray()) {
//         List<Object> elements = new ArrayList<>();
//         for (JsonNode element : value) {
//           elements.add(unwrapJsonValue(element));
//         }
// 
//         callback.bind(key, elements);
//       } else {
//         callback.bind(key, unwrapJsonValue(value));
//       }
//     }
//   }
// 
//   private Object unwrapJsonValue(JsonNode node) {
//     if (node.isNull()) {
//       return null;
//     } else if (node.isBoolean()) {
//       return node.booleanValue();
//     } else if (node.isBinary()) {
//       return ((BinaryNode) node).binaryValue();
//     } else if (node.isNumber()) {
//       return node.numberValue();
//     } else {
//       return node.asText();
//     }
//   }
// }


import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import org.jdbi.v3.sqlobject.customizer.SqlStatementCustomizerFactory;
import org.jdbi.v3.sqlobject.customizer.SqlStatementCustomizingAnnotation;
import org.jdbi.v3.sqlobject.customizer.SqlStatementParameterCustomizer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hubspot.rosetta.RosettaBinder;
import com.hubspot.rosetta.jdbi3.BindWithRosetta.RosettaBinderFactory;
package com.hubspot.rosetta.jdbi3;




@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@SqlStatementCustomizingAnnotation(RosettaBinderFactory.class)
public @interface BindWithRosetta {
  String prefix() default "";

  class RosettaBinderFactory implements SqlStatementCustomizerFactory {

    @Override
    public SqlStatementParameterCustomizer createForParameter(
        Annotation annotation,
        Class<?> sqlObjectType,
        Method method,
        Parameter param,
        int index,
        Type paramType
    ) {
      return (stmt, arg) -> {
        ObjectMapper objectMapper = stmt.getConfig(RosettaObjectMapper.class).getObjectMapper();

        JsonNode node = objectMapper.valueToTree(arg);
        String prefix = ((BindWithRosetta) annotation).prefix();

        if (node.isValueNode() || node.isArray()) {
          node = objectMapper.createObjectNode().set(prefix.isEmpty() ? "it" : prefix, node);
          prefix = "";
        }

        Map<String, Object> namedValues = new HashMap<>();
// Path: RosettaCore/src/main/java/com/hubspot/rosetta/RosettaBinder.java
// public enum RosettaBinder {
//   INSTANCE;
// 
//   public interface Callback {
//     void bind(String key, Object value);
//   }
// 
//   public void bind(String prefix, JsonNode node, Callback callback) {
//     for (Iterator<Entry<String, JsonNode>> iterator = node.fields(); iterator.hasNext(); ) {
//       Entry<String, JsonNode> field = iterator.next();
//       String key = prefix.isEmpty() ? field.getKey() : prefix + "." + field.getKey();
//       JsonNode value = field.getValue();
// 
//       if (value.isObject()) {
//         bind(key, value, callback);
//       } else if (value.isArray()) {
//         List<Object> elements = new ArrayList<>();
//         for (JsonNode element : value) {
//           elements.add(unwrapJsonValue(element));
//         }
// 
//         callback.bind(key, elements);
//       } else {
//         callback.bind(key, unwrapJsonValue(value));
//       }
//     }
//   }
// 
//   private Object unwrapJsonValue(JsonNode node) {
//     if (node.isNull()) {
//       return null;
//     } else if (node.isBoolean()) {
//       return node.booleanValue();
//     } else if (node.isBinary()) {
//       return ((BinaryNode) node).binaryValue();
//     } else if (node.isNumber()) {
//       return node.numberValue();
//     } else {
//       return node.asText();
//     }
//   }
// }


// Path: RosettaJdbi3/src/main/java/com/hubspot/rosetta/jdbi3/BindWithRosetta.java
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import org.jdbi.v3.sqlobject.customizer.SqlStatementCustomizerFactory;
import org.jdbi.v3.sqlobject.customizer.SqlStatementCustomizingAnnotation;
import org.jdbi.v3.sqlobject.customizer.SqlStatementParameterCustomizer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hubspot.rosetta.RosettaBinder;
import com.hubspot.rosetta.jdbi3.BindWithRosetta.RosettaBinderFactory;

package com.hubspot.rosetta.jdbi3;




@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@SqlStatementCustomizingAnnotation(RosettaBinderFactory.class)
public @interface BindWithRosetta {
  String prefix() default "";

  class RosettaBinderFactory implements SqlStatementCustomizerFactory {

    @Override
    public SqlStatementParameterCustomizer createForParameter(
        Annotation annotation,
        Class<?> sqlObjectType,
        Method method,
        Parameter param,
        int index,
        Type paramType
    ) {
      return (stmt, arg) -> {
        ObjectMapper objectMapper = stmt.getConfig(RosettaObjectMapper.class).getObjectMapper();

        JsonNode node = objectMapper.valueToTree(arg);
        String prefix = ((BindWithRosetta) annotation).prefix();

        if (node.isValueNode() || node.isArray()) {
          node = objectMapper.createObjectNode().set(prefix.isEmpty() ? "it" : prefix, node);
          prefix = "";
        }

        Map<String, Object> namedValues = new HashMap<>();
        RosettaBinder.INSTANCE.bind(prefix, node, namedValues::put);