#define _DEFAULT_SOURCE
#include "../runtime/yaza_runtime.c.inc"
/* The runtime include intentionally ends at the yaza_main splice point. */
    return 0;
}

static int check_json(const char *json, int should_parse);

static int check_depth(size_t layers, int should_parse) {
    char json[YAZA_HTTP_MAX_JSON_DEPTH * 2 + 32];
    if (layers * 2 + 2 > sizeof(json)) return 0;
    memset(json, '[', layers);
    json[layers] = '0';
    memset(json + layers + 1, ']', layers);
    json[layers * 2 + 1] = '\0';
    const char *end = yaza_json_skip_value(json);
    if (!should_parse) return end == NULL;
    return end == json + layers * 2 + 1 && *end == '\0';
}

static int check_object_depth(size_t layers, int should_parse) {
    char json[YAZA_HTTP_MAX_JSON_DEPTH * 2 + 32];
    static const char prefix[] = "{\"value\":";
    size_t prefix_length = sizeof(prefix) - 1;
    size_t length = prefix_length + layers * 2 + 3;
    if (length >= sizeof(json)) return 0;
    memcpy(json, prefix, prefix_length);
    memset(json + prefix_length, '[', layers);
    json[prefix_length + layers] = '0';
    memset(json + prefix_length + layers + 1, ']', layers);
    json[length - 2] = '}';
    json[length - 1] = '\0';
    yaza_json_field fields[YAZA_HTTP_MAX_JSON_FIELDS];
    int count = yaza_json_object_fields(json, fields, YAZA_HTTP_MAX_JSON_FIELDS);
    return should_parse ? count == 1 : count == -1;
}

static int check_nested_object_depth(size_t layers, int should_parse) {
    static const char prefix[] = "{\"x\":";
    char json[YAZA_HTTP_MAX_JSON_DEPTH * 6 + 32];
    size_t used = 0;
    for (size_t index = 0; index < layers; index++) {
        memcpy(json + used, prefix, sizeof(prefix) - 1);
        used += sizeof(prefix) - 1;
    }
    json[used++] = '0';
    for (size_t index = 0; index < layers; index++) json[used++] = '}';
    json[used] = '\0';
    return check_json(json, should_parse);
}

static int check_json(const char *json, int should_parse) {
    const char *end = yaza_json_skip_value(json);
    if (!should_parse) return end == NULL;
    return end != NULL && *end == '\0';
}

static int check_nested_field_limit(size_t field_count, int should_parse) {
    char json[2048];
    size_t used = (size_t)snprintf(json, sizeof(json), "{\"nested\":{");
    for (size_t index = 0; index < field_count; index++) {
        int written = snprintf(json + used, sizeof(json) - used, "%s\"k%zu\":%zu",
                              index == 0 ? "" : ",", index, index);
        if (written < 0 || (size_t)written >= sizeof(json) - used) return 0;
        used += (size_t)written;
    }
    int written = snprintf(json + used, sizeof(json) - used, "}}");
    if (written < 0 || (size_t)written >= sizeof(json) - used) return 0;
    return check_json(json, should_parse);
}

int main(void) {
    if (!check_depth(YAZA_HTTP_MAX_JSON_DEPTH - 1, 1)) {
        fprintf(stderr, "JSON parser rejected input within its nesting limit\n");
        return 1;
    }
    if (!check_depth(YAZA_HTTP_MAX_JSON_DEPTH, 0)) {
        fprintf(stderr, "JSON parser accepted input beyond its nesting limit\n");
        return 1;
    }
    if (!check_object_depth(YAZA_HTTP_MAX_JSON_DEPTH - 1, 1)) {
        fprintf(stderr, "JSON object parser rejected a nested value within its limit\n");
        return 1;
    }
    if (!check_object_depth(YAZA_HTTP_MAX_JSON_DEPTH, 0)) {
        fprintf(stderr, "JSON object parser accepted a nested value beyond its limit\n");
        return 1;
    }
    if (!check_nested_object_depth(YAZA_HTTP_MAX_JSON_DEPTH - 1, 1)) {
        fprintf(stderr, "JSON parser rejected nested objects within its depth limit\n");
        return 1;
    }
    if (!check_nested_object_depth(YAZA_HTTP_MAX_JSON_DEPTH, 0)) {
        fprintf(stderr, "JSON parser accepted nested objects beyond its depth limit\n");
        return 1;
    }
    if (!check_json("{\"outer\":[{\"a\":1,\"b\":[true,false]}]}", 1)) {
        fprintf(stderr, "JSON parser rejected a valid nested object\n");
        return 1;
    }
    if (!check_json("{\"outer\":[{\"a\":1,\"a\":2}]}", 0)) {
        fprintf(stderr, "JSON parser accepted duplicate keys in a nested object\n");
        return 1;
    }
    if (!check_json("{\"outer\":{\"a\\u0062\":1,\"c\":2}}", 1)) {
        fprintf(stderr, "JSON parser rejected a valid escaped key\n");
        return 1;
    }
    if (!check_json("{\"outer\":{\"ab\":1,\"a\\u0062\":2}}", 0)) {
        fprintf(stderr, "JSON parser accepted duplicate nested keys with different escapes\n");
        return 1;
    }
    if (!check_json("{\"a\":1,\"\\u0061\":2}", 0)) {
        fprintf(stderr, "JSON parser accepted duplicate top-level keys with different escapes\n");
        return 1;
    }
    yaza_json_field decoded_fields[YAZA_HTTP_MAX_JSON_FIELDS];
    if (yaza_json_object_fields("{\"na\\u006de\":1}", decoded_fields,
                                YAZA_HTTP_MAX_JSON_FIELDS) != 1 ||
        strcmp(decoded_fields[0].name, "name") != 0) {
        fprintf(stderr, "JSON parser did not decode an escaped field name\n");
        return 1;
    }
    if (!check_json("{\"outer\":{\"\\uD83D\\uDE00\":1}}", 1)) {
        fprintf(stderr, "JSON parser rejected a valid surrogate pair in a key\n");
        return 1;
    }
    if (!check_json("{\"outer\":{\"\\uD800\":1}}", 0)) {
        fprintf(stderr, "JSON parser accepted an unpaired Unicode surrogate\n");
        return 1;
    }
    if (!check_json("\"\\uD800\"", 0)) {
        fprintf(stderr, "JSON parser accepted an unpaired surrogate in a string value\n");
        return 1;
    }
    char invalid_utf8[] = {'"', (char)0xc0, (char)0x80, '"', '\0'};
    if (!check_json(invalid_utf8, 0)) {
        fprintf(stderr, "JSON parser accepted invalid UTF-8 in a string\n");
        return 1;
    }
    if (!check_nested_field_limit(YAZA_HTTP_MAX_JSON_FIELDS, 1)) {
        fprintf(stderr, "JSON parser rejected an object within its field limit\n");
        return 1;
    }
    if (!check_nested_field_limit(YAZA_HTTP_MAX_JSON_FIELDS + 1, 0)) {
        fprintf(stderr, "JSON parser accepted too many fields in a nested object\n");
        return 1;
    }
    puts("JSON parser boundary checks passed.");
    return 0;
}
