use serde::{Deserialize, Serialize};
use serde_json::Value;

/// A context item that can be stored in the context blackboard.
///
/// Context items allow tools to communicate with each other by storing
/// key-value pairs that can be retrieved later. The context acts like
/// a blackboard where tools can put information and other tools can
/// read it.
///
/// Values are stored as JSON to ensure they are serializable and
/// human-readable, making them suitable for storage in the event log.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ContextItem {
    /// The key identifying this context item
    pub key: String,
    /// The value stored as JSON, supporting all JSON types:
    /// Object, Array, String, Number, Boolean, Null
    pub value: Value,
}

impl ContextItem {
    /// Create a new context item with the given key and value
    pub fn new(key: String, value: Value) -> Self {
        Self { key, value }
    }

    /// Create a context item from a key and any serializable value
    pub fn from_serializable<T: Serialize>(
        key: String,
        value: T,
    ) -> Result<Self, serde_json::Error> {
        let json_value = serde_json::to_value(value)?;
        Ok(Self::new(key, json_value))
    }

    /// Get the value as a specific type if possible
    pub fn get_value<T: for<'de> Deserialize<'de>>(&self) -> Result<T, serde_json::Error> {
        serde_json::from_value(self.value.clone())
    }

    /// Serialize the value to a JSON string
    pub fn value_to_string(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string(&self.value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_new() {
        // Test line 24 in function `new`
        let item = ContextItem::new("test-key".to_string(), json!("test-value"));
        assert_eq!(item.key, "test-key");
        assert_eq!(item.value, json!("test-value"));
    }

    #[test]
    fn test_new_with_different_types() {
        let string_item = ContextItem::new("string".to_string(), json!("hello"));
        assert_eq!(string_item.key, "string");
        assert_eq!(string_item.value, json!("hello"));

        let number_item = ContextItem::new("number".to_string(), json!(42));
        assert_eq!(number_item.key, "number");
        assert_eq!(number_item.value, json!(42));

        let bool_item = ContextItem::new("bool".to_string(), json!(true));
        assert_eq!(bool_item.key, "bool");
        assert_eq!(bool_item.value, json!(true));

        let null_item = ContextItem::new("null".to_string(), json!(null));
        assert_eq!(null_item.key, "null");
        assert_eq!(null_item.value, json!(null));

        let array_item = ContextItem::new("array".to_string(), json!([1, 2, 3]));
        assert_eq!(array_item.key, "array");
        assert_eq!(array_item.value, json!([1, 2, 3]));

        let object_item = ContextItem::new("object".to_string(), json!({"key": "value"}));
        assert_eq!(object_item.key, "object");
        assert_eq!(object_item.value, json!({"key": "value"}));
    }

    #[test]
    fn test_from_serializable_success() {
        // Test line 29 in function `from_serializable`
        let item = ContextItem::from_serializable("number".to_string(), 42).unwrap();
        assert_eq!(item.key, "number");
        assert_eq!(item.value, json!(42));
    }

    #[test]
    fn test_from_serializable_with_different_types() {
        // Test lines 33-34 in function `from_serializable`

        // Test with string
        let string_item = ContextItem::from_serializable("str".to_string(), "hello").unwrap();
        assert_eq!(string_item.key, "str");
        assert_eq!(string_item.value, json!("hello"));

        // Test with boolean
        let bool_item = ContextItem::from_serializable("bool".to_string(), true).unwrap();
        assert_eq!(bool_item.key, "bool");
        assert_eq!(bool_item.value, json!(true));

        // Test with float
        let float_item = ContextItem::from_serializable("float".to_string(), 1.234).unwrap();
        assert_eq!(float_item.key, "float");
        assert_eq!(float_item.value, json!(1.234));

        // Test with vector
        let vec_item = ContextItem::from_serializable("vec".to_string(), vec![1, 2, 3]).unwrap();
        assert_eq!(vec_item.key, "vec");
        assert_eq!(vec_item.value, json!([1, 2, 3]));

        // Test with struct
        #[derive(serde::Serialize)]
        struct TestStruct {
            name: String,
            age: u32,
        }

        let test_struct = TestStruct {
            name: "Alice".to_string(),
            age: 30,
        };

        let struct_item =
            ContextItem::from_serializable("struct".to_string(), test_struct).unwrap();
        assert_eq!(struct_item.key, "struct");
        assert_eq!(struct_item.value, json!({"name": "Alice", "age": 30}));
    }

    #[test]
    fn test_from_serializable_special_values() {
        // Test with edge cases like NaN, infinity
        let nan_item = ContextItem::from_serializable("nan".to_string(), f64::NAN).unwrap();
        assert_eq!(nan_item.key, "nan");
        assert_eq!(nan_item.value, json!(null)); // NaN serializes to null

        let inf_item = ContextItem::from_serializable("inf".to_string(), f64::INFINITY).unwrap();
        assert_eq!(inf_item.key, "inf");
        assert_eq!(inf_item.value, json!(null)); // Infinity serializes to null
    }

    #[test]
    fn test_get_value_success() {
        // Test lines 38-39 in function `get_value`
        let bool_item = ContextItem::new("test".to_string(), json!(true));
        let value: bool = bool_item.get_value().unwrap();
        assert!(value);

        let string_item = ContextItem::new("text".to_string(), json!("hello"));
        let text: String = string_item.get_value().unwrap();
        assert_eq!(text, "hello");

        let number_item = ContextItem::new("num".to_string(), json!(42));
        let num: i32 = number_item.get_value().unwrap();
        assert_eq!(num, 42);

        let array_item = ContextItem::new("arr".to_string(), json!([1, 2, 3]));
        let arr: Vec<i32> = array_item.get_value().unwrap();
        assert_eq!(arr, vec![1, 2, 3]);
    }

    #[test]
    fn test_get_value_error() {
        // Test error case when deserialization fails
        let string_item = ContextItem::new("text".to_string(), json!("not a number"));
        let result: Result<i32, _> = string_item.get_value();
        assert!(result.is_err());

        let number_item = ContextItem::new("num".to_string(), json!(42));
        let result: Result<String, _> = number_item.get_value();
        assert!(result.is_err());
    }

    #[test]
    fn test_get_value_with_complex_types() {
        #[derive(serde::Deserialize, PartialEq, Debug)]
        struct TestStruct {
            name: String,
            age: u32,
        }

        let complex_item =
            ContextItem::new("complex".to_string(), json!({"name": "Bob", "age": 25}));
        let complex: TestStruct = complex_item.get_value().unwrap();
        assert_eq!(
            complex,
            TestStruct {
                name: "Bob".to_string(),
                age: 25
            }
        );
    }

    #[test]
    fn test_value_to_string_success() {
        // Test lines 43-44 in function `value_to_string`
        let string_item = ContextItem::new("test".to_string(), json!("Hello World"));
        let string_value = string_item.value_to_string().unwrap();
        assert_eq!(string_value, "\"Hello World\"");

        let number_item = ContextItem::new("num".to_string(), json!(42));
        let num_string = number_item.value_to_string().unwrap();
        assert_eq!(num_string, "42");

        let bool_item = ContextItem::new("bool".to_string(), json!(false));
        let bool_string = bool_item.value_to_string().unwrap();
        assert_eq!(bool_string, "false");

        let null_item = ContextItem::new("null".to_string(), json!(null));
        let null_string = null_item.value_to_string().unwrap();
        assert_eq!(null_string, "null");
    }

    #[test]
    fn test_value_to_string_complex() {
        let array_item = ContextItem::new("array".to_string(), json!([1, 2, 3]));
        let array_string = array_item.value_to_string().unwrap();
        assert_eq!(array_string, "[1,2,3]");

        let object_item = ContextItem::new("obj".to_string(), json!({"key": "value", "num": 42}));
        let obj_string = object_item.value_to_string().unwrap();
        // JSON object serialization order may vary, but it should contain both key-value pairs
        assert!(obj_string.contains("\"key\":\"value\""));
        assert!(obj_string.contains("\"num\":42"));
    }

    #[test]
    fn test_context_item_clone() {
        let original = ContextItem::new("test".to_string(), json!({"data": "value"}));
        let cloned = original.clone();

        assert_eq!(original.key, cloned.key);
        assert_eq!(original.value, cloned.value);
    }

    #[test]
    fn test_context_item_partial_eq() {
        let item1 = ContextItem::new("key".to_string(), json!("value"));
        let item2 = ContextItem::new("key".to_string(), json!("value"));
        let item3 = ContextItem::new("key".to_string(), json!("different"));
        let item4 = ContextItem::new("different_key".to_string(), json!("value"));

        assert_eq!(item1, item2);
        assert_ne!(item1, item3);
        assert_ne!(item1, item4);
    }

    #[test]
    fn test_context_item_debug() {
        let item = ContextItem::new("debug_test".to_string(), json!(42));
        let debug_str = format!("{item:?}");

        assert!(debug_str.contains("debug_test"));
        assert!(debug_str.contains("42"));
    }

    #[test]
    fn test_serialization_roundtrip() {
        let original = ContextItem::new("test".to_string(), json!({"nested": {"value": 42}}));

        // Serialize to JSON
        let serialized = serde_json::to_string(&original).unwrap();

        // Deserialize back
        let deserialized: ContextItem = serde_json::from_str(&serialized).unwrap();

        assert_eq!(original, deserialized);
    }

    #[test]
    fn test_empty_key_and_values() {
        // Test with empty key
        let empty_key = ContextItem::new("".to_string(), json!("value"));
        assert_eq!(empty_key.key, "");
        assert_eq!(empty_key.value, json!("value"));

        // Test with empty string value
        let empty_value = ContextItem::new("key".to_string(), json!(""));
        assert_eq!(empty_value.key, "key");
        assert_eq!(empty_value.value, json!(""));

        // Test with empty array
        let empty_array = ContextItem::new("array".to_string(), json!([]));
        assert_eq!(empty_array.key, "array");
        assert_eq!(empty_array.value, json!([]));

        // Test with empty object
        let empty_object = ContextItem::new("object".to_string(), json!({}));
        assert_eq!(empty_object.key, "object");
        assert_eq!(empty_object.value, json!({}));
    }

    #[test]
    fn test_large_values() {
        // Test with large string
        let large_string = "x".repeat(1000);
        let large_item = ContextItem::new("large".to_string(), json!(large_string));
        let retrieved: String = large_item.get_value().unwrap();
        assert_eq!(retrieved.len(), 1000);

        // Test with large array
        let large_array: Vec<i32> = (0..1000).collect();
        let array_item =
            ContextItem::from_serializable("big_array".to_string(), &large_array).unwrap();
        let retrieved_array: Vec<i32> = array_item.get_value().unwrap();
        assert_eq!(retrieved_array.len(), 1000);
        assert_eq!(retrieved_array[999], 999);
    }
}
