/// Comprehensive edge case tests for ToolCall and ToolCallStatus
use dirigent_protocol::types::{ContentBlock, ToolCall, ToolCallContent, ToolCallStatus};
use serde_json::json;

// ===== ToolCallStatus Tests =====

/// Test all ToolCallStatus variants serialize correctly
#[test]
fn test_all_status_variants_serialize() {
    let pending = ToolCallStatus::Pending;
    assert_eq!(serde_json::to_string(&pending).unwrap(), r#""pending""#);

    let running = ToolCallStatus::Running;
    assert_eq!(serde_json::to_string(&running).unwrap(), r#""running""#);

    let completed = ToolCallStatus::Completed;
    assert_eq!(
        serde_json::to_string(&completed).unwrap(),
        r#""completed""#
    );

    let error = ToolCallStatus::Error;
    assert_eq!(serde_json::to_string(&error).unwrap(), r#""error""#);
}

/// Test ToolCallStatus deserialization
#[test]
fn test_status_deserialization() {
    let status: ToolCallStatus = serde_json::from_str(r#""pending""#).unwrap();
    assert_eq!(status, ToolCallStatus::Pending);

    let status: ToolCallStatus = serde_json::from_str(r#""running""#).unwrap();
    assert_eq!(status, ToolCallStatus::Running);

    let status: ToolCallStatus = serde_json::from_str(r#""completed""#).unwrap();
    assert_eq!(status, ToolCallStatus::Completed);

    let status: ToolCallStatus = serde_json::from_str(r#""error""#).unwrap();
    assert_eq!(status, ToolCallStatus::Error);
}

/// Test invalid status deserialization
#[test]
fn test_invalid_status_deserialization() {
    let result: Result<ToolCallStatus, _> = serde_json::from_str(r#""invalid""#);
    assert!(result.is_err(), "Should fail with invalid status");

    let result: Result<ToolCallStatus, _> = serde_json::from_str(r#""PENDING""#);
    assert!(
        result.is_err(),
        "Should fail with uppercase (not snake_case)"
    );
}

/// Test ToolCallStatus roundtrip
#[test]
fn test_status_roundtrip() {
    let statuses = [
        ToolCallStatus::Pending,
        ToolCallStatus::Running,
        ToolCallStatus::Completed,
        ToolCallStatus::Error,
    ];

    for status in statuses {
        let json = serde_json::to_string(&status).unwrap();
        let deserialized: ToolCallStatus = serde_json::from_str(&json).unwrap();
        assert_eq!(status, deserialized);
    }
}

/// Test ToolCallStatus equality and copy
#[test]
fn test_status_equality_and_copy() {
    let status1 = ToolCallStatus::Pending;
    let status2 = status1; // Copy
    assert_eq!(status1, status2);

    let status3 = ToolCallStatus::Running;
    assert_ne!(status1, status3);
}

// ===== ToolCall Minimal Tests =====

/// Test ToolCall with minimal required fields
#[test]
fn test_tool_call_minimal() {
    let tool_call = ToolCall {
        id: "call_min".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Pending,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: None,
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();

    // Required fields present
    assert!(json.contains(r#""id":"call_min""#));
    assert!(json.contains(r#""tool_name":"test""#));
    assert!(json.contains(r#""status":"pending""#));
    assert!(json.contains(r#""content":[]"#));

    // Optional fields not present
    assert!(!json.contains(r#""raw_input""#));
    assert!(!json.contains(r#""raw_output""#));
    assert!(!json.contains(r#""title""#));
    assert!(!json.contains(r#""error""#));
    assert!(!json.contains(r#""metadata""#));
}

/// Test ToolCall with all fields populated
#[test]
fn test_tool_call_maximal() {
    let tool_call = ToolCall {
        id: "call_max".to_string(),
        tool_name: "bash".to_string(),
        status: ToolCallStatus::Completed,
        content: vec![ToolCallContent::from_content_block(ContentBlock::Text {
            text: "Output".to_string(),
        })],
        raw_input: Some(json!({"command": "ls"})),
        raw_output: Some(json!({"exit_code": 0})),
        title: Some("List files".to_string()),
        error: None,
        metadata: Some(json!({"duration_ms": 123})),
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();

    // All fields present
    assert!(json.contains(r#""id":"call_max""#));
    assert!(json.contains(r#""tool_name":"bash""#));
    assert!(json.contains(r#""status":"completed""#));
    assert!(json.contains(r#""content""#));
    assert!(json.contains(r#""raw_input""#));
    assert!(json.contains(r#""raw_output""#));
    assert!(json.contains(r#""title":"List files""#));
    assert!(json.contains(r#""metadata""#));
}

// ===== Edge Cases: Empty Strings =====

/// Test empty tool_name
#[test]
fn test_empty_tool_name() {
    let tool_call = ToolCall {
        id: "call_empty_name".to_string(),
        tool_name: String::new(),
        status: ToolCallStatus::Pending,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: None,
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    assert!(json.contains(r#""tool_name":"""#));

    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.tool_name, "");
}

/// Test empty id
#[test]
fn test_empty_id() {
    let tool_call = ToolCall {
        id: String::new(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Pending,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: None,
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    assert!(json.contains(r#""id":"""#));

    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.id, "");
}

/// Test empty title (Some(""))
#[test]
fn test_empty_title() {
    let tool_call = ToolCall {
        id: "call_empty_title".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Pending,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: Some(String::new()),
        error: None,
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    assert!(json.contains(r#""title":"""#));

    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.title, Some(String::new()));
}

/// Test empty error message (Some(""))
#[test]
fn test_empty_error_message() {
    let tool_call = ToolCall {
        id: "call_empty_error".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Error,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: Some(String::new()),
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    assert!(json.contains(r#""error":"""#));

    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.error, Some(String::new()));
}

// ===== Edge Cases: Large Data =====

/// Test very long error message
#[test]
fn test_long_error_message() {
    let long_error = "Error: ".to_string() + &"x".repeat(10_000);
    let tool_call = ToolCall {
        id: "call_long_error".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Error,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: Some(long_error.clone()),
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.error.unwrap().len(), long_error.len());
}

/// Test large metadata
#[test]
fn test_large_metadata() {
    let large_meta = json!({
        "key1": "value".repeat(1000),
        "key2": [1, 2, 3, 4, 5],
        "nested": {
            "deep": {
                "value": "test"
            }
        }
    });

    let tool_call = ToolCall {
        id: "call_large_meta".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Completed,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: None,
        metadata: Some(large_meta.clone()),
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.metadata, Some(large_meta));
}

/// Test many content blocks
#[test]
fn test_many_content_blocks() {
    let mut content = vec![];
    for i in 0..100 {
        content.push(ToolCallContent::from_content_block(ContentBlock::Text {
            text: format!("Line {}", i),
        }));
    }

    let tool_call = ToolCall {
        id: "call_many_blocks".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Running,
        content: content.clone(),
        raw_input: None,
        raw_output: None,
        title: None,
        error: None,
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.content.len(), 100);
    assert_eq!(deserialized.content, content);
}

// ===== Edge Cases: Special Characters =====

/// Test special characters in tool_name
#[test]
fn test_special_chars_in_tool_name() {
    let tool_call = ToolCall {
        id: "call_special".to_string(),
        tool_name: "bash::execute!@#$%^&*()".to_string(),
        status: ToolCallStatus::Pending,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: None,
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.tool_name, "bash::execute!@#$%^&*()");
}

/// Test unicode in error message
#[test]
fn test_unicode_in_error() {
    let tool_call = ToolCall {
        id: "call_unicode".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Error,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: Some("错误: 文件不存在 🚫".to_string()),
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.error.unwrap(), "错误: 文件不存在 🚫");
}

// ===== Default Content Field =====

/// Test that content defaults to empty vec when not in JSON
#[test]
fn test_content_default() {
    let json = r#"{
        "id": "call_default",
        "tool_name": "test",
        "status": "pending"
    }"#;

    let tool_call: ToolCall = serde_json::from_str(json).unwrap();
    assert_eq!(tool_call.content, vec![]);
}

/// Test that explicit empty content works
#[test]
fn test_explicit_empty_content() {
    let json = r#"{
        "id": "call_explicit",
        "tool_name": "test",
        "status": "pending",
        "content": []
    }"#;

    let tool_call: ToolCall = serde_json::from_str(json).unwrap();
    assert_eq!(tool_call.content, vec![]);
}

// ===== Error Cases =====

/// Test missing required field (id)
#[test]
fn test_missing_id() {
    let json = r#"{
        "tool_name": "test",
        "status": "pending"
    }"#;

    let result: Result<ToolCall, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail without id");
}

/// Test missing required field (tool_name)
#[test]
fn test_missing_tool_name() {
    let json = r#"{
        "id": "call_test",
        "status": "pending"
    }"#;

    let result: Result<ToolCall, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail without tool_name");
}

/// Test missing required field (status)
#[test]
fn test_missing_status() {
    let json = r#"{
        "id": "call_test",
        "tool_name": "test"
    }"#;

    let result: Result<ToolCall, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail without status");
}

/// Test null values for required fields
#[test]
fn test_null_required_fields() {
    let json = r#"{
        "id": null,
        "tool_name": "test",
        "status": "pending"
    }"#;

    let result: Result<ToolCall, _> = serde_json::from_str(json);
    assert!(result.is_err(), "Should fail with null id");
}

/// Test null values for optional fields (should be None)
#[test]
fn test_null_optional_fields() {
    let json = r#"{
        "id": "call_null_opts",
        "tool_name": "test",
        "status": "pending",
        "raw_input": null,
        "raw_output": null,
        "title": null,
        "error": null,
        "metadata": null
    }"#;

    let tool_call: ToolCall = serde_json::from_str(json).unwrap();
    assert!(tool_call.raw_input.is_none());
    assert!(tool_call.raw_output.is_none());
    assert!(tool_call.title.is_none());
    assert!(tool_call.error.is_none());
    assert!(tool_call.metadata.is_none());
}

// ===== Status-Specific Tests =====

/// Test Error status with error message
#[test]
fn test_error_status_with_message() {
    let tool_call = ToolCall {
        id: "call_error".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Error,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: Some("Something went wrong".to_string()),
        metadata: None,
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    assert!(json.contains(r#""status":"error""#));
    assert!(json.contains(r#""error":"Something went wrong""#));

    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.status, ToolCallStatus::Error);
    assert_eq!(
        deserialized.error,
        Some("Something went wrong".to_string())
    );
}

/// Test Completed status with output
#[test]
fn test_completed_status_with_output() {
    let tool_call = ToolCall {
        id: "call_completed".to_string(),
        tool_name: "read".to_string(),
        status: ToolCallStatus::Completed,
        content: vec![ToolCallContent::from_content_block(ContentBlock::Text {
            text: "File contents".to_string(),
        })],
        raw_input: Some(json!({"path": "/tmp/test.txt"})),
        raw_output: Some(json!({"bytes_read": 1024})),
        title: Some("Read file".to_string()),
        error: None,
        metadata: Some(json!({"duration_ms": 42})),
    origin: None,
    };

    let json = serde_json::to_string(&tool_call).unwrap();
    let deserialized: ToolCall = serde_json::from_str(&json).unwrap();

    assert_eq!(deserialized.status, ToolCallStatus::Completed);
    assert!(deserialized.raw_output.is_some());
    assert!(deserialized.error.is_none());
}

// ===== Roundtrip Tests =====

/// Test roundtrip for all status variants
#[test]
fn test_roundtrip_all_statuses() {
    let statuses = [
        ToolCallStatus::Pending,
        ToolCallStatus::Running,
        ToolCallStatus::Completed,
        ToolCallStatus::Error,
    ];

    for status in statuses {
        let tool_call = ToolCall {
            id: format!("call_{:?}", status),
            tool_name: "test".to_string(),
            status,
            content: vec![],
            raw_input: None,
            raw_output: None,
            title: None,
            error: None,
            metadata: None,
        origin: None,
        };

        let json = serde_json::to_string(&tool_call).unwrap();
        let deserialized: ToolCall = serde_json::from_str(&json).unwrap();
        assert_eq!(tool_call, deserialized);
    }
}

// ===== Clone and Debug =====

/// Test ToolCall clone
#[test]
fn test_tool_call_clone() {
    let original = ToolCall {
        id: "call_clone".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Pending,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: None,
        metadata: None,
    origin: None,
    };

    let cloned = original.clone();
    assert_eq!(original, cloned);
}

/// Test ToolCall debug formatting
#[test]
fn test_tool_call_debug() {
    let tool_call = ToolCall {
        id: "call_debug".to_string(),
        tool_name: "test".to_string(),
        status: ToolCallStatus::Pending,
        content: vec![],
        raw_input: None,
        raw_output: None,
        title: None,
        error: None,
        metadata: None,
    origin: None,
    };

    let debug_str = format!("{:?}", tool_call);
    assert!(debug_str.contains("ToolCall"));
    assert!(debug_str.contains("call_debug"));
}
