//! Getter methods for retrieving parameter values.

use super::super::errors::ParamsError;
use super::Params;

impl Params {
    // === String Getters ===

    /// Get string value by key (case-insensitive).
    ///
    /// Returns an error if the key is not found.
    pub fn get(&self, key: &str) -> Result<&str, ParamsError> {
        self.find_param(key)
            .map(|p| p.value.as_str())
            .ok_or_else(|| ParamsError::KeyNotFound(key.to_string()))
    }

    /// Get string value with default if not found.
    pub fn get_default(&self, key: &str, default: &str) -> String {
        self.find_param(key)
            .map(|p| p.value.clone())
            .unwrap_or_else(|| default.to_string())
    }

    /// Get string value, returning None if not found.
    pub fn get_opt(&self, key: &str) -> Option<&str> {
        self.find_param(key).map(|p| p.value.as_str())
    }

    // === Integer Getters ===

    /// Get value as i32.
    pub fn get_int(&self, key: &str) -> Result<i32, ParamsError> {
        let value = self.get(key)?;
        value.parse::<i32>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "i32".to_string(),
            value: value.to_string(),
        })
    }

    /// Get i32 with default.
    pub fn get_int_default(&self, key: &str, default: i32) -> Result<i32, ParamsError> {
        match self.find_param(key) {
            Some(p) if !p.value.is_empty() => {
                p.value
                    .parse::<i32>()
                    .map_err(|_| ParamsError::InvalidValue {
                        key: key.to_string(),
                        expected: "i32".to_string(),
                        value: p.value.clone(),
                    })
            }
            _ => Ok(default),
        }
    }

    /// Get value as u8.
    pub fn get_u8(&self, key: &str) -> Result<u8, ParamsError> {
        let value = self.get(key)?;
        value.parse::<u8>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "u8".to_string(),
            value: value.to_string(),
        })
    }

    /// Get u8 with default.
    pub fn get_u8_default(&self, key: &str, default: u8) -> Result<u8, ParamsError> {
        match self.find_param(key) {
            Some(p) if !p.value.is_empty() => {
                p.value
                    .parse::<u8>()
                    .map_err(|_| ParamsError::InvalidValue {
                        key: key.to_string(),
                        expected: "u8".to_string(),
                        value: p.value.clone(),
                    })
            }
            _ => Ok(default),
        }
    }

    /// Get value as u16.
    pub fn get_u16(&self, key: &str) -> Result<u16, ParamsError> {
        let value = self.get(key)?;
        value.parse::<u16>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "u16".to_string(),
            value: value.to_string(),
        })
    }

    /// Get u16 with default.
    pub fn get_u16_default(&self, key: &str, default: u16) -> Result<u16, ParamsError> {
        match self.find_param(key) {
            Some(p) if !p.value.is_empty() => {
                p.value
                    .parse::<u16>()
                    .map_err(|_| ParamsError::InvalidValue {
                        key: key.to_string(),
                        expected: "u16".to_string(),
                        value: p.value.clone(),
                    })
            }
            _ => Ok(default),
        }
    }

    /// Get value as u32.
    pub fn get_u32(&self, key: &str) -> Result<u32, ParamsError> {
        let value = self.get(key)?;
        value.parse::<u32>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "u32".to_string(),
            value: value.to_string(),
        })
    }

    /// Get u32 with default.
    pub fn get_u32_default(&self, key: &str, default: u32) -> Result<u32, ParamsError> {
        match self.find_param(key) {
            Some(p) if !p.value.is_empty() => {
                p.value
                    .parse::<u32>()
                    .map_err(|_| ParamsError::InvalidValue {
                        key: key.to_string(),
                        expected: "u32".to_string(),
                        value: p.value.clone(),
                    })
            }
            _ => Ok(default),
        }
    }

    /// Get value as u64.
    pub fn get_u64(&self, key: &str) -> Result<u64, ParamsError> {
        let value = self.get(key)?;
        value.parse::<u64>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "u64".to_string(),
            value: value.to_string(),
        })
    }

    /// Get u64 with default.
    pub fn get_u64_default(&self, key: &str, default: u64) -> Result<u64, ParamsError> {
        match self.find_param(key) {
            Some(p) if !p.value.is_empty() => {
                p.value
                    .parse::<u64>()
                    .map_err(|_| ParamsError::InvalidValue {
                        key: key.to_string(),
                        expected: "u64".to_string(),
                        value: p.value.clone(),
                    })
            }
            _ => Ok(default),
        }
    }

    /// Get value as i8.
    pub fn get_i8(&self, key: &str) -> Result<i8, ParamsError> {
        let value = self.get(key)?;
        value.parse::<i8>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "i8".to_string(),
            value: value.to_string(),
        })
    }

    /// Get value as i16.
    pub fn get_i16(&self, key: &str) -> Result<i16, ParamsError> {
        let value = self.get(key)?;
        value.parse::<i16>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "i16".to_string(),
            value: value.to_string(),
        })
    }

    /// Get value as i64.
    pub fn get_i64(&self, key: &str) -> Result<i64, ParamsError> {
        let value = self.get(key)?;
        value.parse::<i64>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "i64".to_string(),
            value: value.to_string(),
        })
    }

    /// Get i64 with default.
    pub fn get_i64_default(&self, key: &str, default: i64) -> Result<i64, ParamsError> {
        match self.find_param(key) {
            Some(p) if !p.value.is_empty() => {
                p.value
                    .parse::<i64>()
                    .map_err(|_| ParamsError::InvalidValue {
                        key: key.to_string(),
                        expected: "i64".to_string(),
                        value: p.value.clone(),
                    })
            }
            _ => Ok(default),
        }
    }

    // === Float Getters ===

    /// Get value as f32.
    pub fn get_f32(&self, key: &str) -> Result<f32, ParamsError> {
        let value = self.get(key)?;
        value.parse::<f32>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "f32".to_string(),
            value: value.to_string(),
        })
    }

    /// Get f32 with default.
    pub fn get_f32_default(&self, key: &str, default: f32) -> Result<f32, ParamsError> {
        match self.find_param(key) {
            Some(p) if !p.value.is_empty() => {
                p.value
                    .parse::<f32>()
                    .map_err(|_| ParamsError::InvalidValue {
                        key: key.to_string(),
                        expected: "f32".to_string(),
                        value: p.value.clone(),
                    })
            }
            _ => Ok(default),
        }
    }

    /// Get value as f64.
    pub fn get_float(&self, key: &str) -> Result<f64, ParamsError> {
        let value = self.get(key)?;
        value.parse::<f64>().map_err(|_| ParamsError::InvalidValue {
            key: key.to_string(),
            expected: "f64".to_string(),
            value: value.to_string(),
        })
    }

    /// Get f64 with default.
    pub fn get_float_default(&self, key: &str, default: f64) -> Result<f64, ParamsError> {
        match self.find_param(key) {
            Some(p) if !p.value.is_empty() => {
                p.value
                    .parse::<f64>()
                    .map_err(|_| ParamsError::InvalidValue {
                        key: key.to_string(),
                        expected: "f64".to_string(),
                        value: p.value.clone(),
                    })
            }
            _ => Ok(default),
        }
    }

    /// Get percentage value (converts "75%" to 0.75, or "0.75" to 0.75).
    pub fn get_percentage(&self, key: &str) -> Result<f64, ParamsError> {
        let value = self.get(key)?;
        let trimmed = value.trim();

        if trimmed.ends_with('%') {
            let num_str = &trimmed[..trimmed.len() - 1];
            let num: f64 = num_str.parse().map_err(|_| ParamsError::InvalidValue {
                key: key.to_string(),
                expected: "percentage".to_string(),
                value: value.to_string(),
            })?;
            Ok(num / 100.0)
        } else {
            let num: f64 = trimmed.parse().map_err(|_| ParamsError::InvalidValue {
                key: key.to_string(),
                expected: "percentage".to_string(),
                value: value.to_string(),
            })?;
            // If > 1, assume it's a percentage value like 75 -> 0.75
            if num > 1.0 {
                Ok(num / 100.0)
            } else {
                Ok(num)
            }
        }
    }

    // === Boolean Getters ===

    /// Returns true if value is empty, "1", "true", "y", "yes".
    /// Returns true if key doesn't exist (default true).
    pub fn get_default_true(&self, key: &str) -> bool {
        match self.find_param(key) {
            Some(p) => {
                let v = p.value.to_lowercase();
                v.is_empty() || v == "1" || v == "true" || v == "y" || v == "yes"
            }
            None => true,
        }
    }

    /// Returns true if value is "1", "true", "y", "yes".
    /// Returns false if key doesn't exist or value is empty (default false).
    pub fn get_default_false(&self, key: &str) -> bool {
        match self.find_param(key) {
            Some(p) => {
                let v = p.value.to_lowercase();
                v == "1" || v == "true" || v == "y" || v == "yes"
            }
            None => false,
        }
    }

    /// Get boolean value, returning error if not found.
    pub fn get_bool(&self, key: &str) -> Result<bool, ParamsError> {
        let value = self.get(key)?;
        let v = value.to_lowercase();
        match v.as_str() {
            "" | "1" | "true" | "y" | "yes" => Ok(true),
            "0" | "false" | "n" | "no" => Ok(false),
            _ => Err(ParamsError::InvalidValue {
                key: key.to_string(),
                expected: "boolean".to_string(),
                value: value.to_string(),
            }),
        }
    }

    // === Existence Checks ===

    /// Check if a key-value parameter exists.
    pub fn exists(&self, key: &str) -> bool {
        self.find_param(key).is_some()
    }

    /// Check if a positional argument exists.
    pub fn exists_arg(&self, arg: &str) -> bool {
        self.args.iter().any(|a| a.eq_ignore_ascii_case(arg))
    }

    /// Check if params is empty (no params, args, or comments).
    pub fn is_empty(&self) -> bool {
        self.params.is_empty() && self.args.is_empty() && self.comments.is_empty()
    }

    /// Get the number of key-value parameters.
    pub fn len(&self) -> usize {
        self.params.len()
    }
}

#[cfg(test)]
mod tests {

    use super::super::parse;

    #[test]
    fn test_get_string() {
        let params = parse("name:john age:30").unwrap();
        assert_eq!(params.get("name").unwrap(), "john");
        assert_eq!(params.get("NAME").unwrap(), "john"); // case insensitive
    }

    #[test]
    fn test_get_default() {
        let params = parse("name:john").unwrap();
        assert_eq!(params.get_default("name", "default"), "john");
        assert_eq!(params.get_default("missing", "default"), "default");
    }

    #[test]
    fn test_get_int() {
        let params = parse("count:42 invalid:abc").unwrap();
        assert_eq!(params.get_int("count").unwrap(), 42);
        assert!(params.get_int("invalid").is_err());
        assert!(params.get_int("missing").is_err());
    }

    #[test]
    fn test_get_int_default() {
        let params = parse("count:42").unwrap();
        assert_eq!(params.get_int_default("count", 0).unwrap(), 42);
        assert_eq!(params.get_int_default("missing", 10).unwrap(), 10);
    }

    #[test]
    fn test_get_float() {
        let params = parse("ratio:3.14").unwrap();
        assert!((params.get_float("ratio").unwrap() - 3.14).abs() < 0.001);
    }

    #[test]
    fn test_get_percentage() {
        let params = parse("percent1:75% percent2:0.75 percent3:75").unwrap();
        assert!((params.get_percentage("percent1").unwrap() - 0.75).abs() < 0.001);
        assert!((params.get_percentage("percent2").unwrap() - 0.75).abs() < 0.001);
        assert!((params.get_percentage("percent3").unwrap() - 0.75).abs() < 0.001);
    }

    #[test]
    fn test_get_bool_defaults() {
        let params = parse("enabled:true disabled:false empty:").unwrap();
        assert!(params.get_default_true("enabled"));
        assert!(!params.get_default_false("disabled"));
        assert!(params.get_default_true("empty")); // empty = true for default_true
        assert!(!params.get_default_false("empty")); // empty = false for default_false
        assert!(params.get_default_true("nonexistent")); // missing = true for default_true
        assert!(!params.get_default_false("nonexistent")); // missing = false for default_false
    }

    #[test]
    fn test_exists() {
        let params = parse("key:value arg1").unwrap();
        assert!(params.exists("key"));
        assert!(!params.exists("missing"));
        assert!(params.exists_arg("arg1"));
        assert!(!params.exists_arg("key"));
    }

    #[test]
    fn test_is_empty() {
        let empty = parse("").unwrap();
        assert!(empty.is_empty());

        let with_params = parse("key:value").unwrap();
        assert!(!with_params.is_empty());
    }
}
