use std::collections::HashMap;

use serde::{
    Deserialize, Serialize,
    de::{DeserializeOwned, Error as DeError},
};
use serde_json::{Map, Value};

use crate::error::Error;

/// Generic argument map used for passing parameters to tools and prompts.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Arguments(pub(crate) Map<String, Value>);

impl Arguments {
    /// Create an empty argument set.
    pub fn new() -> Self {
        Self(Map::new())
    }

    /// Build arguments from any serializable struct.
    pub fn from_struct<T: Serialize>(value: T) -> Result<Self, serde_json::Error> {
        match serde_json::to_value(value)? {
            Value::Object(map) => Ok(Self(map)),
            Value::Null => Ok(Self::new()),
            _ => Err(DeError::custom("arguments must be a struct")),
        }
    }

    /// Insert a single key/value pair, returning the updated `Arguments`.
    ///
    /// This enables fluent construction without an intermediate `HashMap`.
    /// For infallible chaining with common types (strings, numbers, bools),
    /// use [`insert`](Self::insert) instead.
    pub fn set(
        mut self,
        key: impl Into<String>,
        value: impl Serialize,
    ) -> Result<Self, serde_json::Error> {
        let v = serde_json::to_value(value)?;
        self.0.insert(key.into(), v);
        Ok(self)
    }

    /// Insert a JSON value directly, enabling infallible fluent chaining.
    ///
    /// Use this for common types that can be converted to `Value`:
    /// - Strings: `"hello"`, `String::from("hello")`
    /// - Numbers: `42`, `3.14`
    /// - Booleans: `true`, `false`
    /// - Null: `()`
    ///
    /// # Example
    ///
    /// ```ignore
    /// let args = Arguments::new()
    ///     .insert("name", "Alice")
    ///     .insert("count", 42)
    ///     .insert("enabled", true);
    /// ```
    pub fn insert(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
        self.0.insert(key.into(), value.into());
        self
    }

    /// Deserialize the arguments into the desired type.
    pub fn deserialize<T: DeserializeOwned>(self) -> Result<T, serde_json::Error> {
        serde_json::from_value(Value::Object(self.0))
    }

    /// Get a typed value by key.
    pub fn get<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
        self.0
            .get(key)
            .and_then(|v| serde_json::from_value(v.clone()).ok())
    }

    /// Get the raw JSON value for a key.
    pub fn get_value(&self, key: &str) -> Option<&Value> {
        self.0.get(key)
    }

    /// Get a string value by key.
    pub fn get_string(&self, key: &str) -> Option<String> {
        self.get::<String>(key)
    }

    /// Get an i64 value by key.
    pub fn get_i64(&self, key: &str) -> Option<i64> {
        self.get::<i64>(key)
    }

    /// Get a bool value by key.
    pub fn get_bool(&self, key: &str) -> Option<bool> {
        self.get::<bool>(key)
    }

    /// Require a string parameter, returning an error if missing.
    ///
    /// This is a convenience method for the common pattern of extracting
    /// a required string parameter with proper error handling.
    pub fn require_string(&self, key: &str) -> crate::Result<String> {
        self.get_string(key)
            .ok_or_else(|| Error::InvalidParams(format!("missing required parameter: {}", key)))
    }

    /// Require an i64 parameter, returning an error if missing.
    pub fn require_i64(&self, key: &str) -> crate::Result<i64> {
        self.get_i64(key)
            .ok_or_else(|| Error::InvalidParams(format!("missing required parameter: {}", key)))
    }

    /// Require a bool parameter, returning an error if missing.
    pub fn require_bool(&self, key: &str) -> crate::Result<bool> {
        self.get_bool(key)
            .ok_or_else(|| Error::InvalidParams(format!("missing required parameter: {}", key)))
    }

    /// Require a typed parameter, returning an error if missing or wrong type.
    pub fn require<T: DeserializeOwned>(&self, key: &str) -> crate::Result<T> {
        self.get::<T>(key)
            .ok_or_else(|| Error::InvalidParams(format!("missing or invalid parameter: {}", key)))
    }

    /// Deserialize into a typed struct with proper error handling.
    ///
    /// This wraps `deserialize()` to return the crate's `Result` type
    /// with an `InvalidParams` error on failure.
    pub fn into_params<T: DeserializeOwned>(self) -> crate::Result<T> {
        self.deserialize()
            .map_err(|e| Error::InvalidParams(format!("invalid parameters: {}", e)))
    }
}

impl From<HashMap<String, Value>> for Arguments {
    fn from(map: HashMap<String, Value>) -> Self {
        Self(map.into_iter().collect())
    }
}

impl From<HashMap<String, String>> for Arguments {
    fn from(map: HashMap<String, String>) -> Self {
        Self(
            map.into_iter()
                .map(|(k, v)| (k, Value::String(v)))
                .collect(),
        )
    }
}

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

    #[test]
    fn test_require_string() {
        let args = Arguments::new().insert("name", "Alice");

        // Present key
        assert_eq!(args.require_string("name").unwrap(), "Alice");

        // Missing key
        let err = args.require_string("missing").unwrap_err();
        assert!(matches!(err, Error::InvalidParams(_)));
        assert!(err.to_string().contains("missing"));
    }

    #[test]
    fn test_require_i64() {
        let args = Arguments::new().insert("count", 42);

        // Present key
        assert_eq!(args.require_i64("count").unwrap(), 42);

        // Missing key
        let err = args.require_i64("missing").unwrap_err();
        assert!(matches!(err, Error::InvalidParams(_)));
    }

    #[test]
    fn test_require_bool() {
        let args = Arguments::new().insert("enabled", true);

        // Present key
        assert!(args.require_bool("enabled").unwrap());

        // Missing key
        let err = args.require_bool("missing").unwrap_err();
        assert!(matches!(err, Error::InvalidParams(_)));
    }

    #[test]
    fn test_require_typed() {
        let args = Arguments::new().insert("name", "test").insert("count", 5);

        // Present keys
        assert_eq!(args.require::<String>("name").unwrap(), "test");
        assert_eq!(args.require::<i64>("count").unwrap(), 5);

        // Missing key
        let err = args.require::<String>("missing").unwrap_err();
        assert!(matches!(err, Error::InvalidParams(_)));
    }

    #[test]
    fn test_into_params() {
        #[derive(Debug, PartialEq, serde::Deserialize)]
        struct Params {
            name: String,
            count: i64,
        }

        let args = Arguments::new().insert("name", "test").insert("count", 42);

        let params: Params = args.into_params().unwrap();
        assert_eq!(params.name, "test");
        assert_eq!(params.count, 42);
    }

    #[test]
    fn test_into_params_missing_field() {
        #[allow(dead_code)] // Fields are read via serde deserialization
        #[derive(Debug, serde::Deserialize)]
        struct Params {
            name: String,
            count: i64,
        }

        let args = Arguments::new().insert("name", "test");
        // Missing 'count' field

        let err = args.into_params::<Params>().unwrap_err();
        assert!(matches!(err, Error::InvalidParams(_)));
        assert!(err.to_string().contains("count"));
    }
}
