import static java.util.Map.entry;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

sealed interface Json {
    enum JsonNull implements Json { NULL }
    enum JsonBoolean implements Json { TRUE, FALSE }
    record JsonString(String value) implements Json {}
    record JsonNumber(Number value) implements Json {}
    record JsonObject(Map<String, Json> value) implements Json {}
    record JsonArray(List<Json> value) implements Json {}

    default String asString() {
        return switch (this) {
            case JsonNull _ -> "null";
            case JsonBoolean b -> switch (b) {
                case TRUE -> "true";
                case FALSE -> "false";
            };
            case JsonString(var value) -> "\"" + value + "\"";
            case JsonNumber(var value) -> String.valueOf(value);
            case JsonObject(var map)
                -> map.entrySet().stream()
                    .map(entry ->  "\"" + entry.getKey() + "\":" + entry.getValue().asString())
                    .collect(joining(",", "{", "}"));
            case JsonArray(var array)
                -> array.stream()
                    .map(Json::asString)
                    .collect(joining(",", "[", "]"));
        };
    }

    static Json string(String value) {
        return new JsonString(value);
    }

    static Json number(Number value) {
        return new JsonNumber(value);
    }

    static Json array(Json...elements) {
        return new JsonArray(List.of(elements));
    }

    @SafeVarargs
    static Json object(Map.Entry<String, Json>...entries) {
        return new JsonObject(Stream.of(entries)
            .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)));
    }

    static void main() {
        var json = array(
            object(entry("name", string("Toni")), entry("age", number(46)), entry("old", JsonBoolean.TRUE)),
            object(entry("name", string("Baby")), entry("age", JsonNull.NULL), entry("old", JsonBoolean.FALSE))
        );

        System.out.println(json.asString());
    }
}