//! Parameters wrapper for automatic deserialization with JSON Schema support
//!
//! The `Parameters<T>` wrapper enables automatic JSON Schema generation from
//! Rust types when used with the `#[mcp_tool]` macro.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// Transparent wrapper for tool parameters with JSON Schema support
///
/// When used with the `#[mcp_tool]` macro, this wrapper enables:
/// - Automatic JSON Schema generation from the inner type
/// - Type-safe parameter deserialization
/// - Compile-time schema validation
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::Parameters;
/// use schemars::JsonSchema;
/// use serde::Deserialize;
///
/// #[derive(Deserialize, JsonSchema)]
/// struct CalculateParams {
///     /// First operand
///     x: f64,
///     /// Second operand
///     y: f64,
///     /// Operation to perform
///     operation: String,
/// }
///
/// #[mcp_tool]
/// async fn calculate(&self, ctx: Ctx, params: Parameters<CalculateParams>) -> ToolResult {
///     let p = &params.0;
///     Ok(vec![text(&format!("{} + {} = {}", p.x, p.y, p.x + p.y))])
/// }
/// ```
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct Parameters<P>(pub P);

impl<'de, P: serde::de::DeserializeOwned> Deserialize<'de> for Parameters<P> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::de::Error;

        // Deserialize to Value first to handle empty object -> unit type case
        let value = serde_json::Value::deserialize(deserializer)?;

        // If it's an empty object, try deserializing from null (for unit type ())
        // MCP sends {} for tools with no parameters, but () deserializes from null
        if let Some(obj) = value.as_object()
            && obj.is_empty()
                && let Ok(p) = serde_json::from_value::<P>(serde_json::Value::Null) {
                    return Ok(Parameters(p));
                }

        // Otherwise, deserialize normally
        serde_json::from_value::<P>(value)
            .map(Parameters)
            .map_err(D::Error::custom)
    }
}

impl<P> Parameters<P> {
    /// Create a new Parameters wrapper
    pub fn new(inner: P) -> Self {
        Self(inner)
    }

    /// Get a reference to the inner value
    pub fn inner(&self) -> &P {
        &self.0
    }

    /// Get a mutable reference to the inner value
    pub fn inner_mut(&mut self) -> &mut P {
        &mut self.0
    }

    /// Consume the wrapper and return the inner value
    pub fn into_inner(self) -> P {
        self.0
    }
}

impl<P> std::ops::Deref for Parameters<P> {
    type Target = P;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<P> std::ops::DerefMut for Parameters<P> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl<P: JsonSchema> JsonSchema for Parameters<P> {
    fn schema_name() -> std::borrow::Cow<'static, str> {
        P::schema_name()
    }

    fn schema_id() -> std::borrow::Cow<'static, str> {
        P::schema_id()
    }

    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
        P::json_schema(generator)
    }
}

impl<P: Default> Default for Parameters<P> {
    fn default() -> Self {
        Self(P::default())
    }
}

impl<P> From<P> for Parameters<P> {
    fn from(inner: P) -> Self {
        Self(inner)
    }
}

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

    #[derive(Debug, Deserialize, JsonSchema, PartialEq)]
    struct TestParams {
        name: String,
        count: i32,
    }

    #[test]
    fn test_parameters_deref() {
        let params = Parameters(TestParams {
            name: "test".to_string(),
            count: 42,
        });

        assert_eq!(params.name, "test");
        assert_eq!(params.count, 42);
    }

    #[test]
    fn test_parameters_json_schema() {
        let schema = schemars::schema_for!(Parameters<TestParams>);
        let schema_json = serde_json::to_value(schema).unwrap();

        // The schema should have properties for name and count
        assert!(schema_json.get("properties").is_some());
    }

    #[test]
    fn test_parameters_deserialize() {
        let json = r#"{"name": "test", "count": 42}"#;
        let params: Parameters<TestParams> = serde_json::from_str(json).unwrap();

        assert_eq!(params.0.name, "test");
        assert_eq!(params.0.count, 42);
    }

    #[test]
    fn test_parameters_from() {
        let inner = TestParams {
            name: "test".to_string(),
            count: 42,
        };
        let params: Parameters<TestParams> = inner.into();

        assert_eq!(params.0.name, "test");
    }

    #[test]
    fn test_unit_type_schema_is_empty_object() {
        // Unit type () should produce MCP-compliant empty object schema
        // not {"type": "null"}
        let schema = crate::macros::schema_for::<()>();

        assert_eq!(
            schema.get("type"),
            Some(&serde_json::json!("object")),
            "Unit type should produce object schema, not null"
        );
        assert!(
            schema.get("properties").is_some(),
            "Schema should have properties field"
        );
        assert_eq!(
            schema.get("required"),
            Some(&serde_json::json!([])),
            "Schema should have empty required array"
        );
    }

    #[test]
    fn test_parameters_unit_deserialize_from_empty_object() {
        // MCP sends {} for tools with no parameters
        // Parameters<()> should accept both {} and null
        let json = r#"{}"#;
        let params: Parameters<()> = serde_json::from_str(json).unwrap();
        assert_eq!(params.0, ());
    }

    #[test]
    fn test_parameters_unit_deserialize_from_null() {
        // Also accept null for backwards compatibility
        let json = r#"null"#;
        let params: Parameters<()> = serde_json::from_str(json).unwrap();
        assert_eq!(params.0, ());
    }
}
