// tests/suite/test_json.el — comprehensive tests for runtime/json.el
//
// Covers json_get, json_get_raw, json_set, json_array_len, json_array_get,
// json_array_get_string, json_escape_string, json_build_object, json_build_array,
// json_array_push, typed extractors, bytes_to_str, and nested JSON.

// ── json_get ──────────────────────────────────────────────────────────────────

fn test_json_get_basic(_: String) -> String {
    let obj: String = "{\"name\":\"alice\",\"age\":\"30\"}"
    assert_eq(json_get(obj, "name"), "alice", "get string field")
    assert_eq(json_get(obj, "age"), "30", "get numeric-looking string field")
    assert_eq(json_get(obj, "missing"), "", "missing key returns empty string")
    return ""
}

fn test_json_get_types(_: String) -> String {
    let obj: String = "{\"count\":42,\"flag\":true,\"ratio\":3.14}"
    assert_eq(json_get(obj, "count"), "42", "get integer field")
    assert_eq(json_get(obj, "flag"), "true", "get boolean field")
    assert_eq(json_get(obj, "ratio"), "3.14", "get float field")
    assert_eq(json_get(obj, "absent"), "", "absent key yields empty")
    return ""
}

fn test_json_get_nested(_: String) -> String {
    let obj: String = "{\"user\":{\"name\":\"bob\",\"role\":\"admin\"}}"
    assert_eq(json_get(obj, "user.name"), "bob", "dot-path traversal: user.name")
    assert_eq(json_get(obj, "user.role"), "admin", "dot-path traversal: user.role")
    assert_eq(json_get(obj, "user.missing"), "", "missing nested key")
    return ""
}

fn test_json_get_empty(_: String) -> String {
    let empty_obj: String = "{}"
    assert_eq(json_get(empty_obj, "key"), "", "empty object: any key is missing")
    assert_eq(json_get(empty_obj, "a.b"), "", "empty object: nested path missing")
    return ""
}

// ── json_get typed extractors ────────────────────────────────────────────────

fn test_json_get_int_basic(_: String) -> String {
    let obj: String = "{\"count\":42,\"negative\":-7,\"zero\":0}"
    assert_int_eq(json_get_int(obj, "count"), 42, "get int field 42")
    assert_int_eq(json_get_int(obj, "negative"), -7, "get negative int field")
    assert_int_eq(json_get_int(obj, "zero"), 0, "get zero int field")
    assert_int_eq(json_get_int(obj, "missing"), 0, "missing int field returns 0")
    return ""
}

fn test_json_get_bool_basic(_: String) -> String {
    let obj: String = "{\"ok\":true,\"fail\":false}"
    assert_true(json_get_bool(obj, "ok"), "get true bool field")
    assert_false(json_get_bool(obj, "fail"), "get false bool field")
    assert_false(json_get_bool(obj, "missing"), "missing bool field returns false")
    return ""
}

fn test_json_get_float_basic(_: String) -> String {
    let obj: String = "{\"ratio\":3.14,\"negative\":-1.5}"
    let ratio: Float = json_get_float(obj, "ratio")
    assert_true(ratio > 3.13, "ratio > 3.13")
    assert_true(ratio < 3.15, "ratio < 3.15")
    let neg: Float = json_get_float(obj, "negative")
    assert_true(neg < -1.4, "negative float")
    assert_true(neg > -1.6, "negative float bound")
    return ""
}

// ── json_set ──────────────────────────────────────────────────────────────────

fn test_json_set_basic(_: String) -> String {
    let obj: String = "{\"name\":\"alice\"}"
    let obj2: String = json_set(obj, "name", "\"bob\"")
    assert_eq(json_get(obj2, "name"), "bob", "overwrites existing key")

    let obj3: String = json_set(obj, "role", "\"admin\"")
    assert_eq(json_get(obj3, "role"), "admin", "inserts new key")
    assert_eq(json_get(obj3, "name"), "alice", "existing key unchanged")
    return ""
}

fn test_json_set_numeric(_: String) -> String {
    let obj: String = "{}"
    let obj2: String = json_set(obj, "count", "42")
    assert_eq(json_get(obj2, "count"), "42", "set integer value")

    let obj3: String = json_set(obj2, "count", "100")
    assert_eq(json_get(obj3, "count"), "100", "overwrites integer value")
    return ""
}

fn test_json_set_chained(_: String) -> String {
    let obj: String = "{}"
    let obj1: String = json_set(obj, "a", "\"1\"")
    let obj2: String = json_set(obj1, "b", "\"2\"")
    let obj3: String = json_set(obj2, "c", "\"3\"")
    assert_eq(json_get(obj3, "a"), "1", "chained set: a")
    assert_eq(json_get(obj3, "b"), "2", "chained set: b")
    assert_eq(json_get(obj3, "c"), "3", "chained set: c")
    return ""
}

// ── json_array_len ────────────────────────────────────────────────────────────

fn test_json_array_len_basic(_: String) -> String {
    assert_int_eq(json_array_len("[]"), 0, "empty array has length 0")
    assert_int_eq(json_array_len("[1]"), 1, "single element array")
    assert_int_eq(json_array_len("[1,2,3]"), 3, "three element array")
    assert_int_eq(json_array_len("[\"a\",\"b\"]"), 2, "string array length")
    assert_int_eq(json_array_len("[1,2,3,4,5]"), 5, "five element array")
    return ""
}

// ── json_array_get / json_array_get_string ────────────────────────────────────

fn test_json_array_get_basic(_: String) -> String {
    let arr: String = "[\"alpha\",\"beta\",\"gamma\"]"
    assert_eq(json_array_get_string(arr, 0), "alpha", "first element")
    assert_eq(json_array_get_string(arr, 1), "beta", "second element")
    assert_eq(json_array_get_string(arr, 2), "gamma", "third element")
    assert_eq(json_array_get_string(arr, 3), "", "out of bounds returns empty")
    return ""
}

fn test_json_array_get_numbers(_: String) -> String {
    let arr: String = "[10,20,30]"
    assert_eq(json_array_get(arr, 0), "10", "first number as fragment")
    assert_eq(json_array_get(arr, 1), "20", "second number as fragment")
    assert_eq(json_array_get(arr, 2), "30", "third number as fragment")
    assert_int_eq(str_to_int(json_array_get(arr, 0)), 10, "convert fragment to int")
    return ""
}

fn test_json_array_get_objects(_: String) -> String {
    let arr: String = "[{\"name\":\"alice\"},{\"name\":\"bob\"}]"
    let first: String = json_array_get(arr, 0)
    let second: String = json_array_get(arr, 1)
    assert_eq(json_get(first, "name"), "alice", "first object name")
    assert_eq(json_get(second, "name"), "bob", "second object name")
    assert_int_eq(json_array_len(arr), 2, "array of objects has correct length")
    return ""
}

// ── json_escape_string ────────────────────────────────────────────────────────

fn test_json_escape_string_basic(_: String) -> String {
    assert_eq(json_escape_string("hello"), "hello", "plain string unchanged")
    assert_eq(json_escape_string(""), "", "empty string unchanged")
    assert_contains(json_escape_string("say \"hello\""), "\\\"", "double quotes escaped")
    assert_contains(json_escape_string("line1\nline2"), "\\n", "newline escaped")
    assert_contains(json_escape_string("col1\tcol2"), "\\t", "tab escaped")
    return ""
}

fn test_json_escape_string_backslash(_: String) -> String {
    let escaped: String = json_escape_string("a\\b")
    assert_contains(escaped, "\\\\", "backslash doubled")
    assert_true(str_len(escaped) > 3, "escaped is longer than original")
    return ""
}

fn test_json_escape_roundtrip(_: String) -> String {
    // Build a JSON string with an escaped value and extract it back
    let raw: String = "hello world"
    let escaped: String = json_escape_string(raw)
    let json_str: String = "{\"msg\":\"" + escaped + "\"}"
    assert_eq(json_get(json_str, "msg"), raw, "roundtrip: escape then parse")
    return ""
}

// ── json_build_object ─────────────────────────────────────────────────────────

fn test_json_build_object_basic(_: String) -> String {
    let parts: [String] = el_list_empty()
    let parts = el_list_append(parts, "name")
    let parts = el_list_append(parts, "alice")
    let parts = el_list_append(parts, "role")
    let parts = el_list_append(parts, "admin")
    let obj: String = json_build_object(parts)
    assert_eq(json_get(obj, "name"), "alice", "build object: name field")
    assert_eq(json_get(obj, "role"), "admin", "build object: role field")
    assert_true(str_starts_with(obj, "{"), "build object starts with {")
    assert_true(str_ends_with(obj, "}"), "build object ends with }")
    return ""
}

fn test_json_build_object_empty(_: String) -> String {
    let parts: [String] = el_list_empty()
    let obj: String = json_build_object(parts)
    assert_true(str_starts_with(obj, "{"), "empty build object starts with {")
    assert_true(str_ends_with(obj, "}"), "empty build object ends with }")
    return ""
}

// ── json_build_array ──────────────────────────────────────────────────────────

fn test_json_build_array_basic(_: String) -> String {
    let items: [String] = el_list_empty()
    let items = el_list_append(items, "\"alice\"")
    let items = el_list_append(items, "\"bob\"")
    let arr: String = json_build_array(items)
    assert_int_eq(json_array_len(arr), 2, "built array has 2 elements")
    assert_eq(json_array_get_string(arr, 0), "alice", "first element of built array")
    assert_eq(json_array_get_string(arr, 1), "bob", "second element of built array")
    return ""
}

fn test_json_build_array_empty(_: String) -> String {
    let items: [String] = el_list_empty()
    let arr: String = json_build_array(items)
    assert_int_eq(json_array_len(arr), 0, "empty built array has 0 elements")
    assert_eq(arr, "[]", "empty array is []")
    return ""
}

fn test_json_build_array_numbers(_: String) -> String {
    let items: [String] = el_list_empty()
    let items = el_list_append(items, "1")
    let items = el_list_append(items, "2")
    let items = el_list_append(items, "3")
    let arr: String = json_build_array(items)
    assert_int_eq(json_array_len(arr), 3, "numeric array has 3 elements")
    assert_eq(json_array_get(arr, 0), "1", "first numeric element")
    assert_eq(json_array_get(arr, 2), "3", "last numeric element")
    return ""
}

// ── json_array_push ───────────────────────────────────────────────────────────

fn test_json_array_push_basic(_: String) -> String {
    let arr: String = "[]"
    let arr2: String = json_array_push(arr, "\"hello\"")
    assert_int_eq(json_array_len(arr2), 1, "after push: length 1")
    assert_eq(json_array_get_string(arr2, 0), "hello", "pushed element accessible")

    let arr3: String = json_array_push(arr2, "\"world\"")
    assert_int_eq(json_array_len(arr3), 2, "after second push: length 2")
    assert_eq(json_array_get_string(arr3, 1), "world", "second pushed element")
    return ""
}

fn test_json_array_push_preserves_order(_: String) -> String {
    let arr: String = "[\"a\",\"b\"]"
    let arr2: String = json_array_push(arr, "\"c\"")
    assert_eq(json_array_get_string(arr2, 0), "a", "first element preserved")
    assert_eq(json_array_get_string(arr2, 1), "b", "second element preserved")
    assert_eq(json_array_get_string(arr2, 2), "c", "pushed element at end")
    assert_int_eq(json_array_len(arr2), 3, "total length after push")
    return ""
}

// ── nested JSON ───────────────────────────────────────────────────────────────

fn test_json_nested_set_get(_: String) -> String {
    let obj: String = "{\"user\":{\"name\":\"alice\"},\"count\":1}"
    assert_eq(json_get(obj, "user.name"), "alice", "deep get via dot path")
    assert_eq(json_get(obj, "count"), "1", "top-level int field")

    let obj2: String = json_set(obj, "count", "2")
    assert_eq(json_get(obj2, "count"), "2", "updated top-level field")
    assert_eq(json_get(obj2, "user.name"), "alice", "nested field still accessible after set")
    return ""
}

fn test_json_array_of_objects(_: String) -> String {
    let arr: String = "[{\"id\":1,\"name\":\"alice\"},{\"id\":2,\"name\":\"bob\"}]"
    assert_int_eq(json_array_len(arr), 2, "array of objects: length 2")
    let first: String = json_array_get(arr, 0)
    let second: String = json_array_get(arr, 1)
    assert_eq(json_get(first, "name"), "alice", "first object name")
    assert_eq(json_get(second, "name"), "bob", "second object name")
    assert_eq(json_get(first, "id"), "1", "first object id")
    assert_eq(json_get(second, "id"), "2", "second object id")
    return ""
}
