//! Shared utilities for the fetch module.
//!
//! Provides common helper functions used across multiple fetch submodules.

/// Serializes a value to JSON string.
///
/// Note: JSON object key order is not guaranteed by the JSON spec, but `serde_json`
/// typically preserves insertion order for `HashMap`. If deterministic key ordering
/// is required, use `BTreeMap` in the source data structure instead.
///
/// # Errors
///
/// If serialization fails, logs a warning and returns an empty JSON object `"{}"`.
#[allow(dead_code)] // Used in tests
pub(crate) fn serialize_json<T: serde::Serialize>(value: &T) -> String {
    serde_json::to_string(value).unwrap_or_else(|e| {
        log::warn!(
            "Failed to serialize value to JSON: {}. Using default: {{}}",
            e
        );
        "{}".to_string()
    })
}

/// Serializes a value to JSON string with a custom default for errors.
///
/// Useful for arrays where we want "[]" instead of "{}" on serialization failure.
///
/// # Arguments
///
/// * `value` - The value to serialize
/// * `default` - The default string to return if serialization fails
///
/// # Errors
///
/// If serialization fails, logs a warning and returns the provided default.
pub(crate) fn serialize_json_with_default<T: serde::Serialize>(value: &T, default: &str) -> String {
    serde_json::to_string(value).unwrap_or_else(|e| {
        log::warn!(
            "Failed to serialize value to JSON: {}. Using default: {}",
            e,
            default
        );
        default.to_string()
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    #[test]
    fn test_serialize_json_success() {
        let mut map = HashMap::new();
        map.insert("key1", "value1");
        map.insert("key2", "value2");
        let json = serialize_json(&map);
        assert!(json.contains("key1"));
        assert!(json.contains("value1"));
        assert!(json.contains("key2"));
        assert!(json.contains("value2"));
    }

    #[test]
    fn test_serialize_json_array() {
        let vec = vec!["item1", "item2", "item3"];
        let json = serialize_json(&vec);
        assert_eq!(json, r#"["item1","item2","item3"]"#);
    }

    #[test]
    fn test_serialize_json_with_default_success() {
        let vec = vec!["item1", "item2"];
        let json = serialize_json_with_default(&vec, "[]");
        assert_eq!(json, r#"["item1","item2"]"#);
    }

    #[test]
    fn test_serialize_json_with_default_custom() {
        let vec = vec!["item1"];
        let json = serialize_json_with_default(&vec, "[]");
        assert_eq!(json, r#"["item1"]"#);
    }

    #[test]
    fn test_serialize_json_with_default_empty_array() {
        let vec: Vec<String> = vec![];
        let json = serialize_json_with_default(&vec, "[]");
        assert_eq!(json, "[]");
    }

    #[test]
    fn test_serialize_json_with_default_custom_default() {
        let vec: Vec<String> = vec!["test".to_string()];
        let json = serialize_json_with_default(&vec, "fallback");
        // Should serialize successfully, not use fallback
        assert!(json.contains("test"));
        assert!(!json.contains("fallback"));
    }

    #[test]
    fn test_serialize_json_empty_map() {
        let map: HashMap<String, String> = HashMap::new();
        let json = serialize_json(&map);
        assert_eq!(json, "{}");
    }

    #[test]
    fn test_serialize_json_with_default_nested_structure() {
        #[derive(serde::Serialize)]
        struct Nested {
            inner: Vec<String>,
        }
        let nested = Nested {
            inner: vec!["a".to_string(), "b".to_string()],
        };
        let json = serialize_json_with_default(&nested, "{}");
        assert!(json.contains("inner"));
        assert!(json.contains("a"));
        assert!(json.contains("b"));
    }

    #[test]
    fn test_serialize_json_with_default_null_values() {
        use serde_json::json;
        let value = json!({
            "key1": "value1",
            "key2": null,
            "key3": ["item1", null, "item2"]
        });
        let json = serialize_json_with_default(&value, "{}");
        assert!(json.contains("key1"));
        assert!(json.contains("key2"));
        assert!(json.contains("null")); // null values should be serialized
    }

    #[test]
    fn test_serialize_json_very_large_structure() {
        // Test with very large structure (should not panic or hang)
        let mut large_map: HashMap<String, String> = HashMap::new();
        for i in 0..10000 {
            large_map.insert(format!("key_{}", i), format!("value_{}", i));
        }
        let json = serialize_json(&large_map);
        // Should serialize successfully (may be large but should complete)
        assert!(json.len() > 1000);
        assert!(json.contains("key_0"));
        assert!(json.contains("value_9999"));
    }

    #[test]
    fn test_serialize_json_with_default_very_large_array() {
        // Test with very large array
        let large_vec: Vec<String> = (0..10000).map(|i| format!("item_{}", i)).collect();
        let json = serialize_json_with_default(&large_vec, "[]");
        assert!(json.len() > 1000);
        assert!(json.contains("item_0"));
        assert!(json.contains("item_9999"));
    }

    #[test]
    fn test_serialize_json_nested_structures() {
        #[derive(serde::Serialize)]
        struct Nested {
            level1: Level1,
        }
        #[derive(serde::Serialize)]
        struct Level1 {
            level2: Level2,
        }
        #[derive(serde::Serialize)]
        struct Level2 {
            value: String,
        }

        let nested = Nested {
            level1: Level1 {
                level2: Level2 {
                    value: "deep".to_string(),
                },
            },
        };
        let json = serialize_json(&nested);
        assert!(json.contains("deep"));
        assert!(json.contains("level1"));
        assert!(json.contains("level2"));
    }

    #[test]
    fn test_serialize_json_with_default_empty_string() {
        let empty: Vec<String> = vec![];
        let json = serialize_json_with_default(&empty, "fallback");
        // Empty array should serialize to "[]", not use fallback
        assert_eq!(json, "[]");
    }

    #[test]
    fn test_serialize_json_special_characters() {
        let mut map = HashMap::new();
        map.insert(
            "key".to_string(),
            "value with \"quotes\" and 'apostrophes'".to_string(),
        );
        map.insert("unicode".to_string(), "测试 🚀".to_string());
        let json = serialize_json(&map);
        assert!(json.contains("quotes"));
        assert!(json.contains("测试"));
        assert!(json.contains("🚀"));
    }

    #[test]
    fn test_serialize_json_with_default_error_path() {
        // Test that the error handling path returns the correct default
        // We can't easily trigger a serialization failure without creating a custom type,
        // but we can verify the default value logic is correct
        let vec: Vec<String> = vec!["test".to_string()];
        let json = serialize_json_with_default(&vec, "fallback");
        // Should serialize successfully, not use fallback
        assert!(json.contains("test"));
        assert!(!json.contains("fallback"));

        // Verify default is used when provided (even if serialization succeeds)
        // The function should use the default only on failure, so this test verifies
        // the default parameter is correctly passed through
        let json2 = serialize_json_with_default(&vec, "[]");
        // Should serialize successfully, not use "[]" default
        assert!(json2.contains("test"));
        assert!(!json2.contains("[]") || json2 == r#"["test"]"#); // May contain "[]" as part of array
    }

    #[test]
    fn test_serialize_json_error_returns_empty_object() {
        // Verify that serialize_json returns "{}" on error (default behavior)
        // This is critical - the error handling must return a valid JSON object
        let valid_map: HashMap<String, String> = HashMap::new();
        let json = serialize_json(&valid_map);
        // Empty map should serialize to "{}"
        assert_eq!(json, "{}");

        // This verifies the default return value is correct
        // Actual serialization failures are hard to trigger, but the default
        // value "{}" is verified to be returned for empty maps
    }

    #[test]
    fn test_serialize_json_error_handling_path() {
        // Test the error handling path by creating a type that fails to serialize
        // This is critical - error handling must work correctly when serialization fails
        use serde::ser::{Error, Serialize, Serializer};

        // Create a type that always fails to serialize
        struct FailingSerialize;

        impl Serialize for FailingSerialize {
            fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer,
            {
                Err(S::Error::custom(
                    "Intentional serialization failure for testing",
                ))
            }
        }

        // Test that serialize_json handles the error gracefully
        let failing_value = FailingSerialize;
        let json = serialize_json(&failing_value);
        // Should return "{}" (the default) when serialization fails
        assert_eq!(
            json, "{}",
            "serialize_json should return default '{{}}' on serialization failure"
        );
    }

    #[test]
    fn test_serialize_json_with_default_error_handling_path() {
        // Test that serialize_json_with_default uses the provided default on error
        // This is critical - the default parameter must be used when serialization fails
        use serde::ser::{Error, Serialize, Serializer};

        // Create a type that always fails to serialize
        struct FailingSerialize;

        impl Serialize for FailingSerialize {
            fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer,
            {
                Err(S::Error::custom(
                    "Intentional serialization failure for testing",
                ))
            }
        }

        // Test with custom default "[]"
        let failing_value = FailingSerialize;
        let json = serialize_json_with_default(&failing_value, "[]");
        // Should return the provided default "[]" when serialization fails
        assert_eq!(
            json, "[]",
            "serialize_json_with_default should return the provided default on serialization failure"
        );

        // Test with custom default "fallback"
        let json2 = serialize_json_with_default(&failing_value, "fallback");
        assert_eq!(
            json2, "fallback",
            "serialize_json_with_default should return the provided default on serialization failure"
        );
    }
}
