/// Comprehensive edge case tests for ContentBlock
use dirigent_protocol::types::ContentBlock;

/// Test empty string in Text variant
#[test]
fn test_text_empty_string() {
    let block = ContentBlock::Text {
        text: String::new(),
    };
    let json = serde_json::to_string(&block).unwrap();
    assert!(json.contains(r#""type":"text"#));
    assert!(json.contains(r#""text":"""#));

    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
    assert_eq!(block, deserialized);
}

/// Test very long string (>10KB) in Text variant
#[test]
fn test_text_very_long_string() {
    let long_text = "a".repeat(10_000);
    let block = ContentBlock::Text {
        text: long_text.clone(),
    };
    let json = serde_json::to_string(&block).unwrap();
    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();

    match deserialized {
        ContentBlock::Text { text } => {
            assert_eq!(text.len(), 10_000);
            assert_eq!(text, long_text);
        }
        _ => panic!("Expected Text variant"),
    }
}

/// Test special characters (unicode, emojis, newlines) in Text
#[test]
fn test_text_special_characters() {
    let special_text = "Hello 👋\nWorld 🌍\t中文\r\n\"quotes\" and 'apostrophes' \\ backslash";
    let block = ContentBlock::Text {
        text: special_text.to_string(),
    };
    let json = serde_json::to_string(&block).unwrap();
    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();

    match deserialized {
        ContentBlock::Text { text } => {
            assert_eq!(text, special_text);
        }
        _ => panic!("Expected Text variant"),
    }
}

/// Test JSON control characters in Text
#[test]
fn test_text_json_control_characters() {
    let control_chars = "Line1\nLine2\r\nLine3\tTabbed\x08Backspace";
    let block = ContentBlock::Text {
        text: control_chars.to_string(),
    };
    let json = serde_json::to_string(&block).unwrap();
    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();

    match deserialized {
        ContentBlock::Text { text } => {
            assert_eq!(text, control_chars);
        }
        _ => panic!("Expected Text variant"),
    }
}

/// Test ResourceLink with empty URI
#[test]
fn test_resource_link_empty_uri() {
    let block = ContentBlock::ResourceLink {
        uri: String::new(),
        name: None,
        mime_type: None,
    };
    let json = serde_json::to_string(&block).unwrap();
    assert!(json.contains(r#""type":"resource_link"#));
    assert!(json.contains(r#""uri":"""#));

    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
    assert_eq!(block, deserialized);
}

/// Test ResourceLink with all optional fields as None
#[test]
fn test_resource_link_all_none() {
    let block = ContentBlock::ResourceLink {
        uri: "file:///test.txt".to_string(),
        name: None,
        mime_type: None,
    };
    let json = serde_json::to_string(&block).unwrap();

    // Verify optional fields are NOT in JSON
    assert!(!json.contains(r#""name""#));
    assert!(!json.contains(r#""mime_type""#));

    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
    assert_eq!(block, deserialized);
}

/// Test ResourceLink with all optional fields as Some
#[test]
fn test_resource_link_all_some() {
    let block = ContentBlock::ResourceLink {
        uri: "https://example.com/file.pdf".to_string(),
        name: Some("document.pdf".to_string()),
        mime_type: Some("application/pdf".to_string()),
    };
    let json = serde_json::to_string(&block).unwrap();

    // Verify all fields ARE in JSON
    assert!(json.contains(r#""uri":"https://example.com/file.pdf"#));
    assert!(json.contains(r#""name":"document.pdf"#));
    assert!(json.contains(r#""mime_type":"application/pdf"#));

    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
    assert_eq!(block, deserialized);
}

/// Test ResourceLink with only name
#[test]
fn test_resource_link_only_name() {
    let block = ContentBlock::ResourceLink {
        uri: "file:///path/to/file".to_string(),
        name: Some("my_file".to_string()),
        mime_type: None,
    };
    let json = serde_json::to_string(&block).unwrap();

    assert!(json.contains(r#""name":"my_file"#));
    assert!(!json.contains(r#""mime_type""#));

    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
    assert_eq!(block, deserialized);
}

/// Test ResourceLink with only mime_type
#[test]
fn test_resource_link_only_mime_type() {
    let block = ContentBlock::ResourceLink {
        uri: "file:///path/to/file".to_string(),
        name: None,
        mime_type: Some("text/plain".to_string()),
    };
    let json = serde_json::to_string(&block).unwrap();

    assert!(!json.contains(r#""name""#));
    assert!(json.contains(r#""mime_type":"text/plain"#));

    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
    assert_eq!(block, deserialized);
}

/// Test ResourceLink with empty optional strings
#[test]
fn test_resource_link_empty_optional_strings() {
    let block = ContentBlock::ResourceLink {
        uri: "file:///test".to_string(),
        name: Some(String::new()),
        mime_type: Some(String::new()),
    };
    let json = serde_json::to_string(&block).unwrap();

    // Empty strings should still serialize
    assert!(json.contains(r#""name":"""#));
    assert!(json.contains(r#""mime_type":"""#));

    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
    assert_eq!(block, deserialized);
}

/// Test ResourceLink with special characters in URI
#[test]
fn test_resource_link_special_uri() {
    let uri = "file:///path/to/file%20with%20spaces.txt?query=param&other=value#fragment";
    let block = ContentBlock::ResourceLink {
        uri: uri.to_string(),
        name: Some("file with spaces.txt".to_string()),
        mime_type: Some("text/plain; charset=utf-8".to_string()),
    };
    let json = serde_json::to_string(&block).unwrap();
    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();

    match deserialized {
        ContentBlock::ResourceLink {
            uri: deser_uri,
            name,
            mime_type,
        } => {
            assert_eq!(deser_uri, uri);
            assert_eq!(name, Some("file with spaces.txt".to_string()));
            assert_eq!(mime_type, Some("text/plain; charset=utf-8".to_string()));
        }
        _ => panic!("Expected ResourceLink variant"),
    }
}

/// Test ResourceLink with very long URI
#[test]
fn test_resource_link_long_uri() {
    let long_path = format!("file:///{}", "a/".repeat(500));
    let block = ContentBlock::ResourceLink {
        uri: long_path.clone(),
        name: Some("file.txt".to_string()),
        mime_type: None,
    };
    let json = serde_json::to_string(&block).unwrap();
    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();

    match deserialized {
        ContentBlock::ResourceLink { uri, .. } => {
            assert_eq!(uri, long_path);
        }
        _ => panic!("Expected ResourceLink variant"),
    }
}

/// Test deserialization from JSON without type field (should fail)
#[test]
fn test_deserialization_missing_type_field() {
    let json = r#"{"text": "Hello"}"#;
    let result: Result<ContentBlock, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail without type field");
}

/// Test deserialization with invalid type value
#[test]
fn test_deserialization_invalid_type() {
    let json = r#"{"type": "invalid_type", "text": "Hello"}"#;
    let result: Result<ContentBlock, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail with invalid type");
}

/// Test deserialization Text without text field (should fail)
#[test]
fn test_deserialization_text_missing_field() {
    let json = r#"{"type": "text"}"#;
    let result: Result<ContentBlock, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail without text field");
}

/// Test deserialization ResourceLink without uri field (should fail)
#[test]
fn test_deserialization_resource_link_missing_uri() {
    let json = r#"{"type": "resource_link", "name": "file.txt"}"#;
    let result: Result<ContentBlock, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail without uri field");
}

/// Test that type tag is snake_case
#[test]
fn test_type_tag_format() {
    let text = ContentBlock::Text {
        text: "test".to_string(),
    };
    let json = serde_json::to_string(&text).unwrap();
    assert!(json.contains(r#""type":"text"#));
    assert!(!json.contains(r#""type":"Text"#));

    let resource = ContentBlock::ResourceLink {
        uri: "file:///test".to_string(),
        name: None,
        mime_type: None,
    };
    let json = serde_json::to_string(&resource).unwrap();
    assert!(json.contains(r#""type":"resource_link"#));
    assert!(!json.contains(r#""type":"ResourceLink"#));
}

/// Test roundtrip with all ContentBlock variants
#[test]
fn test_all_variants_roundtrip() {
    let variants = vec![
        ContentBlock::Text {
            text: "Test text".to_string(),
        },
        ContentBlock::ResourceLink {
            uri: "file:///test.txt".to_string(),
            name: Some("test.txt".to_string()),
            mime_type: Some("text/plain".to_string()),
        },
        ContentBlock::ResourceLink {
            uri: "https://example.com/resource".to_string(),
            name: None,
            mime_type: None,
        },
    ];

    for variant in variants {
        let json = serde_json::to_string(&variant).unwrap();
        let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
        assert_eq!(variant, deserialized);
    }
}

/// Test pretty-printed JSON
#[test]
fn test_pretty_json() {
    let block = ContentBlock::ResourceLink {
        uri: "file:///test.txt".to_string(),
        name: Some("test.txt".to_string()),
        mime_type: Some("text/plain".to_string()),
    };
    let json = serde_json::to_string_pretty(&block).unwrap();

    // Should be parseable
    let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
    assert_eq!(block, deserialized);
}

/// Test null values in JSON (should fail for required fields)
#[test]
fn test_null_values() {
    let json = r#"{"type": "text", "text": null}"#;
    let result: Result<ContentBlock, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail with null text");

    let json = r#"{"type": "resource_link", "uri": null}"#;
    let result: Result<ContentBlock, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail with null uri");
}

/// Test null values for optional fields (should deserialize as None)
#[test]
fn test_null_optional_fields() {
    let json = r#"{
        "type": "resource_link",
        "uri": "file:///test",
        "name": null,
        "mime_type": null
    }"#;
    let result: ContentBlock = serde_json::from_str(json).unwrap();

    match result {
        ContentBlock::ResourceLink {
            uri,
            name,
            mime_type,
        } => {
            assert_eq!(uri, "file:///test");
            assert!(name.is_none());
            assert!(mime_type.is_none());
        }
        _ => panic!("Expected ResourceLink"),
    }
}

/// Test ContentBlock Clone and PartialEq
#[test]
fn test_clone_and_equality() {
    let original = ContentBlock::Text {
        text: "Test".to_string(),
    };
    let cloned = original.clone();
    assert_eq!(original, cloned);

    let different = ContentBlock::Text {
        text: "Different".to_string(),
    };
    assert_ne!(original, different);
}

/// Test ContentBlock Debug formatting
#[test]
fn test_debug_formatting() {
    let block = ContentBlock::Text {
        text: "Debug test".to_string(),
    };
    let debug_str = format!("{:?}", block);
    assert!(debug_str.contains("Text"));
    assert!(debug_str.contains("Debug test"));
}
