package bindings;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import java.io.IOException;
import java.sql.SQLException;
import org.jooq.BindingSQLContext;
import org.jooq.impl.DSL;
import org.jooq.Converter;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Custom data binding for JSONB data type in postgres using JOOQ.
 * Details at: https://www.jooq.org/doc/3.9/manual/code-generation/custom-data-type-bindings/
 */
public class PostgresJsonBinding extends AbstractPostgresBinding {

    @Override
    public Converter<Object, JsonNode> converter() {
        return new Converter<Object, JsonNode>() {
            /**
             * This overrides parent behavior and returns null rather then  "{}" when null is found
             */
            @Override
            public JsonNode from(Object t) {
                try {
                    return t == null ? null : new ObjectMapper().readTree(t.toString());
                } catch (IOException e) {
                    return JsonNodeFactory.instance.objectNode();
                }
            }

            @Override
            public Object to(JsonNode u) {
                return u == null || u == JsonNodeFactory.instance.objectNode() ? null : u.toString();
            }

            @Override
            public Class<Object> fromType() {
                return Object.class;
            }

            @Override
            public Class<JsonNode> toType() {
                return JsonNode.class;
            }
        };
    }
    @Override
    public void sql(BindingSQLContext<JsonNode> ctx) throws SQLException {

        // Rendering a bind variable for the binding context's value and casting it to the json type
        //Allows for null values to be stored in DB
        if (ctx.convert(converter()).value() == null) {
            ctx.render().visit(DSL.val(ctx.convert(converter()).value(), JsonNode.class));
        } else {
            ctx.render().visit(DSL.val(ctx.convert(converter()).value())).sql("::json");
        }
    }

}
