//! Partial JSON Parser Tests
//!
//! Tests for the partial JSON parser with allow_partial_strings flag behavior

use sglang_router_rs::tool_parser::partial_json::PartialJson;

#[test]
fn test_partial_string_flag_disallows_incomplete_strings() {
    // Test case from the bug report: {"name": "
    // With allow_partial_strings=false, should return {} (stop before incomplete string)
    let parser = PartialJson::new(32, true);
    let input = r#"{"name": ""#;

    let result = parser.parse_value(input, false);
    assert!(result.is_ok());

    let (obj, consumed) = result.unwrap();

    // Should parse just the opening brace and stop at the incomplete string
    assert!(obj.is_object());
    let obj_map = obj.as_object().unwrap();

    // Should have empty object (stopped before parsing incomplete "name" key)
    assert!(
        obj_map.is_empty() || !obj_map.contains_key("name"),
        "Should not parse incomplete string key, got: {:?}",
        obj_map
    );

    // Should consume characters up to the incomplete string
    assert!(consumed <= input.len());
}

#[test]
fn test_partial_string_flag_allows_incomplete_strings() {
    // Test case: {"name": "
    // With allow_partial_strings=true, should parse the incomplete string
    let parser = PartialJson::new(32, true);
    let input = r#"{"name": ""#;

    let result = parser.parse_value(input, true);
    assert!(result.is_ok());

    let (obj, consumed) = result.unwrap();

    // Should parse the object with incomplete string value
    assert!(obj.is_object());
    let obj_map = obj.as_object().unwrap();

    // With allow_partial_strings=true, should parse "name" key with empty string value
    assert!(
        obj_map.contains_key("name"),
        "Should parse incomplete string with allow_partial_strings=true"
    );

    assert_eq!(consumed, input.len());
}

#[test]
fn test_partial_string_flag_complete_json() {
    // Test case: {"name": "test"}
    // Both flags should parse complete JSON the same way
    let input = r#"{"name": "test"}"#;

    let parser = PartialJson::new(32, true);
    let result1 = parser.parse_value(input, false);
    assert!(result1.is_ok());
    let (obj1, consumed1) = result1.unwrap();

    let result2 = parser.parse_value(input, true);
    assert!(result2.is_ok());
    let (obj2, consumed2) = result2.unwrap();

    // Both should parse the same complete JSON
    assert_eq!(obj1, obj2);
    assert_eq!(consumed1, consumed2);
    assert_eq!(consumed1, input.len());

    // Check the parsed value
    assert!(obj1.is_object());
    let obj_map = obj1.as_object().unwrap();
    assert_eq!(obj_map.get("name").and_then(|v| v.as_str()), Some("test"));
}

#[test]
fn test_backward_compatibility_default() {
    // Test that default PartialJson still allows partial strings (backward compatible)
    let parser = PartialJson::default();
    let input = r#"{"name": ""#;

    let result = parser.parse_value(input, true);
    assert!(result.is_ok());

    let (obj, _) = result.unwrap();
    assert!(obj.is_object());

    // Default behavior should allow partial strings
    let obj_map = obj.as_object().unwrap();
    assert!(
        obj_map.contains_key("name"),
        "Default should allow partial strings for backward compatibility"
    );
}

#[test]
fn test_partial_string_in_nested_object() {
    // Test case: {"tool": {"name": "
    let parser = PartialJson::new(32, true);
    let input = r#"{"tool": {"name": ""#;

    let result = parser.parse_value(input, false);
    assert!(result.is_ok());

    let (obj, _) = result.unwrap();
    assert!(obj.is_object());

    // With allow_partial_strings=false, should stop before incomplete nested string
    let obj_map = obj.as_object().unwrap();
    if let Some(tool) = obj_map.get("tool") {
        if let Some(tool_map) = tool.as_object() {
            assert!(
                !tool_map.contains_key("name")
                    || tool_map.get("name").and_then(|v| v.as_str()).is_none(),
                "Should not parse incomplete nested string"
            );
        }
    }
}

#[test]
fn test_bug_fix_exact_scenario() {
    // This test verifies the exact bug scenario from the issue:
    // buffer = "{\"name\": \""
    // flags = Allow.ALL & ~Allow.STR
    // Python returns: Parsed object: {}, consumed length: 10

    let parser = PartialJson::new(32, true);
    let input = r#"{"name": ""#;

    let result = parser.parse_value(input, false);
    assert!(result.is_ok());

    let (obj, consumed) = result.unwrap();

    // Should return empty object (not {"name": null} or {"name": ""})
    assert!(obj.is_object());
    let obj_map = obj.as_object().unwrap();
    assert!(
        obj_map.is_empty(),
        "Expected empty object, got: {:?}. This matches Python behavior with Allow.ALL & ~Allow.STR",
        obj_map
    );

    // Should consume all characters (10 bytes)
    assert_eq!(consumed, 10, "Should consume all 10 characters");
}
