// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import "utils/Test.sol";

library JsonStructs {
    address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
    Vm constant vm = Vm(HEVM_ADDRESS);

    // forge eip712 testdata/default/cheats/Json.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^FlatJson
    string constant schema_FlatJson =
        "FlatJson(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";

    // forge eip712 testdata/default/cheats/Json.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^NestedJson
    string constant schema_NestedJson =
        "NestedJson(FlatJson[] members,AnotherFlatJson inner,string name)AnotherFlatJson(bytes4 fixedBytes)FlatJson(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";

    function deserializeFlatJson(string memory json) internal pure returns (ParseJsonTest.FlatJson memory) {
        return abi.decode(vm.parseJsonType(json, schema_FlatJson), (ParseJsonTest.FlatJson));
    }

    function deserializeFlatJson(string memory json, string memory path)
        internal
        pure
        returns (ParseJsonTest.FlatJson memory)
    {
        return abi.decode(vm.parseJsonType(json, path, schema_FlatJson), (ParseJsonTest.FlatJson));
    }

    function deserializeFlatJsonArray(string memory json, string memory path)
        internal
        pure
        returns (ParseJsonTest.FlatJson[] memory)
    {
        return abi.decode(vm.parseJsonTypeArray(json, path, schema_FlatJson), (ParseJsonTest.FlatJson[]));
    }

    function deserializeNestedJson(string memory json) internal pure returns (ParseJsonTest.NestedJson memory) {
        return abi.decode(vm.parseJsonType(json, schema_NestedJson), (ParseJsonTest.NestedJson));
    }

    function deserializeNestedJson(string memory json, string memory path)
        internal
        pure
        returns (ParseJsonTest.NestedJson memory)
    {
        return abi.decode(vm.parseJsonType(json, path, schema_NestedJson), (ParseJsonTest.NestedJson));
    }

    function deserializeNestedJsonArray(string memory json, string memory path)
        internal
        pure
        returns (ParseJsonTest.NestedJson[] memory)
    {
        return abi.decode(vm.parseJsonType(json, path, schema_NestedJson), (ParseJsonTest.NestedJson[]));
    }

    function serialize(ParseJsonTest.FlatJson memory instance) internal pure returns (string memory) {
        return vm.serializeJsonType(schema_FlatJson, abi.encode(instance));
    }

    function serialize(ParseJsonTest.NestedJson memory instance) internal pure returns (string memory) {
        return vm.serializeJsonType(schema_NestedJson, abi.encode(instance));
    }
}

contract ParseJsonTest is Test {
    using JsonStructs for *;

    struct StaticArray {
        uint256[1] timestamp;
    }

    struct NestedStaticArray {
        uint256[2][1] matrix;
    }

    struct AmbiguousAStaticArray {
        uint256[1] ambiguousValues;
    }

    struct AmbiguousZDynamicArray {
        uint256[] ambiguousValues;
    }

    struct StaticBytesArray {
        bytes4[1] selectors;
    }

    struct SelectiveStaticArray {
        uint256[1] fixedValues;
        string inferredAddress;
    }

    struct StaticStructChild {
        uint256 value;
        string label;
    }

    struct StaticStructParent {
        StaticStructChild[1] children;
    }

    struct NestedStaticStructChild {
        uint256[1] values;
    }

    struct NestedStaticStructParent {
        NestedStaticStructChild[2][1] nestedChildren;
    }

    struct FlatJson {
        uint256 a;
        int24[][] arr;
        string str;
        bytes b;
        address addr;
        bytes32 fixedBytes;
    }

    struct AnotherFlatJson {
        bytes4 fixedBytes;
    }

    struct NestedJson {
        FlatJson[] members;
        AnotherFlatJson inner;
        string name;
    }

    string json;

    function setUp() public {
        string memory path = "fixtures/Json/test.json";
        json = vm.readFile(path);
    }

    function test_basicString() public {
        bytes memory data = vm.parseJson(json, ".basicString");
        string memory decodedData = abi.decode(data, (string));
        assertEq("hai", decodedData);
    }

    function test_parseJsonComments() public {
        string memory jsonc = vm.readFile("fixtures/Json/comments.jsonc");

        assertEq(vm.parseJsonUint(jsonc, ".value"), 42);
        assertTrue(vm.parseJsonBool(jsonc, ".enabled"));
        assertEq(vm.parseJsonString(jsonc, ".url"), "https://example.com/path/*literal*/");
    }

    function test_null() public {
        bytes memory data = vm.parseJson(json, ".null");
        bytes memory decodedData = abi.decode(data, (bytes));
        assertEq(new bytes(0), decodedData);
    }

    function test_stringArray() public {
        bytes memory data = vm.parseJson(json, ".stringArray");
        string[] memory decodedData = abi.decode(data, (string[]));
        assertEq("hai", decodedData[0]);
        assertEq("there", decodedData[1]);
    }

    function test_address() public {
        bytes memory data = vm.parseJson(json, ".address");
        address decodedData = abi.decode(data, (address));
        assertEq(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, decodedData);
    }

    function test_addressArray() public {
        bytes memory data = vm.parseJson(json, ".addressArray");
        address[] memory decodedData = abi.decode(data, (address[]));
        assertEq(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, decodedData[0]);
        assertEq(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D, decodedData[1]);
    }

    function test_H160ButNotaddress() public {
        string memory data = abi.decode(vm.parseJson(json, ".H160NotAddress"), (string));
        assertEq("0000000000000000000000000000000000001337", data);
    }

    function test_bool() public {
        bytes memory data = vm.parseJson(json, ".boolTrue");
        bool decodedData = abi.decode(data, (bool));
        assertTrue(decodedData);

        data = vm.parseJson(json, ".boolFalse");
        decodedData = abi.decode(data, (bool));
        assertTrue(!decodedData);
    }

    function test_boolArray() public {
        bytes memory data = vm.parseJson(json, ".boolArray");
        bool[] memory decodedData = abi.decode(data, (bool[]));
        assertTrue(decodedData[0]);
        assertTrue(!decodedData[1]);
    }

    function test_uintArray() public {
        bytes memory data = vm.parseJson(json, ".uintArray");
        uint256[] memory decodedData = abi.decode(data, (uint256[]));
        assertEq(42, decodedData[0]);
        assertEq(43, decodedData[1]);
    }

    function test_staticArray() public {
        bytes memory data = vm.parseJson('{"timestamp":[1655140035]}');
        StaticArray memory decodedData = abi.decode(data, (StaticArray));
        assertEq(1655140035, decodedData.timestamp[0]);
    }

    function test_nestedStaticArray() public {
        bytes memory data = vm.parseJson('{"matrix":[[42,43]]}');
        NestedStaticArray memory decodedData = abi.decode(data, (NestedStaticArray));
        assertEq(42, decodedData.matrix[0][0]);
        assertEq(43, decodedData.matrix[0][1]);
    }

    function test_staticArrayLengthMismatch() public {
        vm._expectCheatcodeRevert("array length mismatch");
        vm.parseJson('{"timestamp":[1,2]}');
    }

    function test_ambiguousArrayFallsBackToDynamic() public {
        bytes memory data = vm.parseJson('{"ambiguousValues":[42]}');
        AmbiguousZDynamicArray memory decodedData = abi.decode(data, (AmbiguousZDynamicArray));
        assertEq(42, decodedData.ambiguousValues[0]);
    }

    function test_staticArrayUsesElementType() public {
        bytes memory data = vm.parseJson('{"selectors":["0x12345678"]}');
        StaticBytesArray memory decodedData = abi.decode(data, (StaticBytesArray));
        assertEq(bytes32(decodedData.selectors[0]), bytes32(bytes4(0x12345678)));
    }

    function test_staticArrayPreservesSiblingInference() public {
        address inferredAddress = address(0x1234);
        bytes memory data =
            vm.parseJson(string.concat('{"fixedValues":[42],"inferredAddress":"', vm.toString(inferredAddress), '"}'));
        uint256[1] memory fixedValues = [uint256(42)];
        assertEq(data, abi.encode(fixedValues, inferredAddress));
    }

    function test_staticStructArray() public {
        bytes memory data = vm.parseJson('{"children":[{"label":"child","value":42}]}');
        StaticStructParent memory decodedData = abi.decode(data, (StaticStructParent));
        assertEq(42, decodedData.children[0].value);
        assertEq("child", decodedData.children[0].label);
    }

    function test_nestedStaticStructArray() public {
        bytes memory data = vm.parseJson('{"nestedChildren":[[{"values":[42]},{"values":[43]}]]}');
        NestedStaticStructParent memory decodedData = abi.decode(data, (NestedStaticStructParent));
        assertEq(42, decodedData.nestedChildren[0][0].values[0]);
        assertEq(43, decodedData.nestedChildren[0][1].values[0]);
    }

    function test_staticStructArrayLengthMismatch() public {
        vm._expectCheatcodeRevert("array length mismatch");
        vm.parseJson('{"children":[]}');
    }

    // Object keys are sorted alphabetically, regardless of input.
    struct Whole {
        string str;
        string[] strArray;
        uint256[] uintArray;
    }

    function test_wholeObject() public {
        // we need to make the path relative to the crate that's running tests for it (forge crate)
        string memory path = "fixtures/Json/whole_json.json";
        console.log(path);
        json = vm.readFile(path);
        bytes memory data = vm.parseJson(json);
        Whole memory whole = abi.decode(data, (Whole));
        assertEq(whole.str, "hai");
        assertEq(whole.uintArray[0], 42);
        assertEq(whole.uintArray[1], 43);
        assertEq(whole.strArray[0], "hai");
        assertEq(whole.strArray[1], "there");
    }

    function test_coercionRevert() public {
        vm._expectCheatcodeRevert("expected uint256, found JSON object");
        vm.parseJsonUint(json, ".nestedObject");
    }

    function test_coercionUint() public {
        uint256 number = vm.parseJsonUint(json, ".uintHex");
        assertEq(number, 1231232);
        number = vm.parseJsonUint(json, ".uintString");
        assertEq(number, 115792089237316195423570985008687907853269984665640564039457584007913129639935);
        number = vm.parseJsonUint(json, ".uintNumber");
        assertEq(number, 115792089237316195423570985008687907853269984665640564039457584007913129639935);
        uint256[] memory numbers = vm.parseJsonUintArray(json, ".uintArray");
        assertEq(numbers[0], 42);
        assertEq(numbers[1], 43);
        numbers = vm.parseJsonUintArray(json, ".uintStringArray");
        assertEq(numbers[0], 1231232);
        assertEq(numbers[1], 1231232);
        assertEq(numbers[2], 1231232);
    }

    function test_coercionInt() public {
        int256 number = vm.parseJsonInt(json, ".intNumber");
        assertEq(number, -12);
        number = vm.parseJsonInt(json, ".intString");
        assertEq(number, -12);
        number = vm.parseJsonInt(json, ".intHex");
        assertEq(number, -12);
    }

    function test_coercionBool() public {
        bool boolean = vm.parseJsonBool(json, ".boolTrue");
        assertTrue(boolean);
        bool boolFalse = vm.parseJsonBool(json, ".boolFalse");
        assertTrue(!boolFalse);
        boolean = vm.parseJsonBool(json, ".boolString");
        assertEq(boolean, true);
        bool[] memory booleans = vm.parseJsonBoolArray(json, ".boolArray");
        assertTrue(booleans[0]);
        assertTrue(!booleans[1]);
        booleans = vm.parseJsonBoolArray(json, ".boolStringArray");
        assertTrue(booleans[0]);
        assertTrue(!booleans[1]);
    }

    function test_coercionBytes() public {
        bytes memory bytes_ = vm.parseJsonBytes(json, ".bytesString");
        assertEq(bytes_, hex"01");

        bytes[] memory bytesArray = vm.parseJsonBytesArray(json, ".bytesStringArray");
        assertEq(bytesArray[0], hex"01");
        assertEq(bytesArray[1], hex"02");
    }

    struct Nested {
        uint256 number;
        string str;
    }

    function test_nestedObject() public {
        bytes memory data = vm.parseJson(json, ".nestedObject");
        Nested memory nested = abi.decode(data, (Nested));
        assertEq(nested.number, 115792089237316195423570985008687907853269984665640564039457584007913129639935);
        assertEq(nested.str, "NEST");
    }

    function test_advancedJsonPath() public {
        bytes memory data = vm.parseJson(json, ".advancedJsonPath[*].id");
        uint256[] memory numbers = abi.decode(data, (uint256[]));
        assertEq(numbers[0], 1);
        assertEq(numbers[1], 2);
    }

    function test_canonicalizePath() public {
        bytes memory data = vm.parseJson(json, "$.basicString");
        string memory decodedData = abi.decode(data, (string));
        assertEq("hai", decodedData);
    }

    function test_nonExistentKey() public {
        bytes memory data = vm.parseJson(json, ".thisKeyDoesNotExist");
        assertEq(0, data.length);
    }

    function test_parseJsonKeys() public {
        string memory jsonString =
            '{"some_key_to_value": "some_value", "some_key_to_array": [1, 2, 3], "some_key_to_object": {"key1": "value1", "key2": 2}}';

        string[] memory keys = vm.parseJsonKeys(jsonString, "$");
        string[] memory expected = new string[](3);
        expected[0] = "some_key_to_value";
        expected[1] = "some_key_to_array";
        expected[2] = "some_key_to_object";
        assertEq(abi.encode(keys), abi.encode(expected));

        keys = vm.parseJsonKeys(jsonString, ".some_key_to_object");
        expected = new string[](2);
        expected[0] = "key1";
        expected[1] = "key2";
        assertEq(abi.encode(keys), abi.encode(expected));

        vm._expectCheatcodeRevert("JSON value at \".some_key_to_array\" is not an object");
        vm.parseJsonKeys(jsonString, ".some_key_to_array");

        vm._expectCheatcodeRevert("JSON value at \".some_key_to_value\" is not an object");
        vm.parseJsonKeys(jsonString, ".some_key_to_value");

        vm._expectCheatcodeRevert("key \".*\" must return exactly one JSON object");
        vm.parseJsonKeys(jsonString, ".*");
    }

    function test_parseJsonArrayLength() public {
        string memory jsonString = '{"array":[1,{"nested":true},"three"],"empty":[],"object":{"array":[1,2]}}';

        assertEq(vm.parseJsonArrayLength("[1,2,3]", "$"), 3);
        assertEq(vm.parseJsonArrayLength(jsonString, ".array"), 3);
        assertEq(vm.parseJsonArrayLength(jsonString, ".empty"), 0);
        assertEq(vm.parseJsonArrayLength(jsonString, ".object.array"), 2);

        vm._expectCheatcodeRevert('JSON value at ".object" is not an array');
        vm.parseJsonArrayLength(jsonString, ".object");

        vm._expectCheatcodeRevert('JSON value at ".array[0]" is not an array');
        vm.parseJsonArrayLength(jsonString, ".array[0]");

        vm._expectCheatcodeRevert('key ".missing" must return exactly one JSON array');
        vm.parseJsonArrayLength(jsonString, ".missing");

        vm._expectCheatcodeRevert('key ".array[*]" must return exactly one JSON array');
        vm.parseJsonArrayLength(jsonString, ".array[*]");
    }

    // forge eip712 testdata/default/cheats/Json.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^FlatJson
    string constant schema_FlatJson =
        "FlatJson(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";

    // forge eip712 testdata/default/cheats/Json.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^NestedJson
    string constant schema_NestedJson =
        "NestedJson(FlatJson[] members,AnotherFlatJson inner,string name)AnotherFlatJson(bytes4 fixedBytes)FlatJson(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";

    function test_parseJsonType() public {
        string memory readJson = vm.readFile("fixtures/Json/nested_json_struct.json");
        NestedJson memory data = readJson.deserializeNestedJson();
        assertEq(data.members.length, 2);

        FlatJson memory expected = FlatJson({
            a: 200,
            arr: new int24[][](0),
            str: "some other string",
            b: hex"0000000000000000000000000000000000000000",
            addr: 0x167D91deaEEE3021161502873d3bcc6291081648,
            fixedBytes: 0xed1c7beb1f00feaaaec5636950d6edb25a8d4fedc8deb2711287b64c4d27719d
        });

        assertEq(keccak256(abi.encode(data.members[1])), keccak256(abi.encode(expected)));
        assertEq(bytes32(data.inner.fixedBytes), bytes32(bytes4(0x12345678)));

        FlatJson[] memory members = JsonStructs.deserializeFlatJsonArray(readJson, ".members");

        assertEq(keccak256(abi.encode(members)), keccak256(abi.encode(data.members)));
    }

    function test_parseJsonType_roundtrip() public {
        string memory readJson = vm.readFile("fixtures/Json/nested_json_struct.json");
        NestedJson memory data = readJson.deserializeNestedJson();
        string memory serialized = data.serialize();
        NestedJson memory deserialized = serialized.deserializeNestedJson();
        assertEq(keccak256(abi.encode(data)), keccak256(abi.encode(deserialized)));
    }
}

contract WriteJsonTest is Test {
    string json1;
    string json2;

    function setUp() public {
        json1 = "example";
        json2 = "example2";
    }

    function test_serializeJson() public {
        vm.serializeUint(json1, "key1", uint256(254));
        vm.serializeBool(json1, "boolean", true);
        vm.serializeInt(json2, "key2", -234);
        vm.serializeUint(json2, "deploy", uint256(254));
        vm.serializeUintToHex(json2, "hexUint", uint256(255));
        string memory data = vm.serializeBool(json2, "boolean", true);
        vm.serializeString(json2, "json1", data);
        emit log(data);
    }

    function test_serializeArray() public {
        bool[] memory data1 = new bool[](3);
        data1[0] = true;
        data1[2] = false;
        vm.serializeBool(json1, "array1", data1);

        address[] memory data2 = new address[](3);
        data2[0] = address(0xBEEEF);
        data2[2] = vm.addr(123);
        vm.serializeAddress(json1, "array2", data2);

        bytes[] memory data3 = new bytes[](3);
        data3[0] = bytes("123");
        data3[2] = bytes("fpovhpgjaiosfjhapiufpsdf");
        string memory finalJson = vm.serializeBytes(json1, "array3", data3);

        string memory path = "fixtures/Json/write_test_array.json";
        vm.writeJson(finalJson, path);

        string memory json = vm.readFile(path);
        bytes memory rawData = vm.parseJson(json, ".array1");
        bool[] memory parsedData1 = new bool[](3);
        parsedData1 = abi.decode(rawData, (bool[]));
        assertEq(parsedData1[0], data1[0]);
        assertEq(parsedData1[1], data1[1]);
        assertEq(parsedData1[2], data1[2]);

        rawData = vm.parseJson(json, ".array2");
        address[] memory parsedData2 = new address[](3);
        parsedData2 = abi.decode(rawData, (address[]));
        assertEq(parsedData2[0], data2[0]);
        assertEq(parsedData2[1], data2[1]);
        assertEq(parsedData2[2], data2[2]);

        rawData = vm.parseJson(json, ".array3");
        bytes[] memory parsedData3 = new bytes[](3);
        parsedData3 = abi.decode(rawData, (bytes[]));
        assertEq(parsedData3[0], data3[0]);
        assertEq(parsedData3[1], data3[1]);
        assertEq(parsedData3[2], data3[2]);
        vm.removeFile(path);
    }

    // The serializeJson cheatcode was added to support assigning an existing json string to an object key.
    // Github issue: https://github.com/foundry-rs/foundry/issues/5745
    function test_serializeRootObject() public {
        string memory serialized = vm.serializeJson(json1, '{"foo": "bar"}');
        assertEq(serialized, '{"foo": "bar"}');
        serialized = vm.serializeBool(json1, "boolean", true);
        assertEq(vm.parseJsonString(serialized, ".foo"), "bar");
        assertEq(vm.parseJsonBool(serialized, ".boolean"), true);

        string memory overwritten = vm.serializeJson(json1, '{"value": 123}');
        assertEq(overwritten, '{"value": 123}');
    }

    struct simpleJson {
        uint256 a;
        string b;
    }

    struct notSimpleJson {
        uint256 a;
        string b;
        simpleJson c;
    }

    function test_serializeNotSimpleJson() public {
        string memory json3 = "json3";
        string memory path = "fixtures/Json/write_complex_test.json";
        vm.serializeUint(json3, "a", uint256(123));
        string memory semiFinal = vm.serializeString(json3, "b", "test");
        string memory finalJson = vm.serializeString(json3, "c", semiFinal);
        console.log(finalJson);
        vm.writeJson(finalJson, path);
        string memory json = vm.readFile(path);
        bytes memory data = vm.parseJson(json);
        notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson));
    }

    function test_retrieveEntireJson() public {
        string memory path = "fixtures/Json/write_complex_test.json";
        string memory json = vm.readFile(path);
        bytes memory data = vm.parseJson(json, ".");
        notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson));
        console.log(decodedData.a);
        assertEq(decodedData.a, 123);
    }

    function test_checkKeyExistsJson() public {
        string memory path = "fixtures/Json/write_complex_test.json";
        string memory json = vm.readFile(path);
        bool exists = vm.keyExistsJson(json, ".a");
        assertTrue(exists);

        // TODO: issue deprecation warning
        exists = vm.keyExists(json, ".a");
        assertTrue(exists);
    }

    function test_checkKeyDoesNotExistJson() public {
        string memory path = "fixtures/Json/write_complex_test.json";
        string memory json = vm.readFile(path);
        bool exists = vm.keyExistsJson(json, ".d");
        assertTrue(!exists);

        // TODO: issue deprecation warning
        exists = vm.keyExists(json, ".d");
        assertTrue(!exists);
    }

    function test_writeJson() public {
        string memory json3 = "json3";
        string memory path = "fixtures/Json/write_test.json";
        vm.serializeUint(json3, "a", uint256(123));
        string memory finalJson = vm.serializeString(json3, "b", "test");
        vm.writeJson(finalJson, path);

        string memory json = vm.readFile(path);
        bytes memory data = vm.parseJson(json);
        simpleJson memory decodedData = abi.decode(data, (simpleJson));
        assertEq(decodedData.a, 123);
        assertEq(decodedData.b, "test");

        // write json3 to key b
        vm.writeJson(finalJson, path, ".b");
        // read again
        json = vm.readFile(path);
        data = vm.parseJson(json, ".b");
        decodedData = abi.decode(data, (simpleJson));
        assertEq(decodedData.a, 123);
        assertEq(decodedData.b, "test");

        // replace a single value to key b
        address ex = address(0xBEEF);
        vm.writeJson(vm.toString(ex), path, ".b");
        json = vm.readFile(path);
        data = vm.parseJson(json, ".b");
        address decodedAddress = abi.decode(data, (address));
        assertEq(decodedAddress, ex);
    }

    function test_writeJson_createKeys() public {
        string memory path = "fixtures/Json/write_test.json";
        string memory json = vm.readFile(path);

        bool exists = vm.keyExistsJson(json, ".parent");
        assertTrue(!exists);
        exists = vm.keyExistsJson(json, ".parent.child");
        assertTrue(!exists);
        exists = vm.keyExistsJson(json, ".parent.child.value");
        assertTrue(!exists);

        // Write to nested path, creating intermediate keys
        vm.writeJson(vm.toString(uint256(42)), path, ".parent.child.value");

        // Verify the value was written and intermediate keys were created
        json = vm.readFile(path);
        uint256 value = abi.decode(vm.parseJson(json, ".parent.child.value"), (uint256));
        assertEq(value, 42);

        // Clean up the test file by removing the parent key we added
        vm.removeFile(path);
        vm.writeJson("{\"a\": 123, \"b\": \"0x000000000000000000000000000000000000bEEF\"}", path);
    }

    function test_writeJson_createFile() public {
        string memory path = "fixtures/Json/write_test_nonexistent.json";

        // Write to a file that does not exist using the 3-arg overload
        vm.writeJson(vm.toString(uint256(99)), path, ".x.y");

        // Verify the file was created with the correct content
        string memory json = vm.readFile(path);
        uint256 value = abi.decode(vm.parseJson(json, ".x.y"), (uint256));
        assertEq(value, 99);

        // Clean up
        vm.removeFile(path);
    }
}
