use crate::prelude::*;
use paste::paste;

#[derive(Debug)]
pub(crate) enum Value {
    Boolean(bool),
    Integer(i64),
    Float(f64),
    String(String),
    StringVec(Vec<String>),
}
pub(crate) trait ConstantDefault: 'static {
    fn constant_default() -> &'static Self;
}

macro_rules! impl_value_traits {
    ($($target:ty => $variant:ident / $default:expr),*) => {
        $(
            impl<'a> TryFrom<&'a Value> for &'a $target {
                type Error = Error;
                fn try_from(value: &'a Value) -> std::result::Result<Self, Self::Error> {
                    if let Value::$variant(v) = value {
                        Ok(v)
                    } else {
                        Err(anyhow!("Cannot cast {value:?} to {}.", stringify!($target)))
                    }
                }
            }
            paste! {
                static [<CONSTANT_DEFAULT_ $target:upper>]: $target = $default;
                impl ConstantDefault for $target {
                    fn constant_default() -> &'static Self
                    {
                        &[<CONSTANT_DEFAULT_ $target:upper>]
                    }
                }
            }
        )*
    };
}

type VecString = Vec<String>;

// Manual bool TryFrom — only accept Boolean variant.
static CONSTANT_DEFAULT_BOOL: bool = false;

impl<'a> TryFrom<&'a Value> for &'a bool {
    type Error = Error;
    fn try_from(value: &'a Value) -> std::result::Result<Self, Self::Error> {
        if let Value::Boolean(v) = value {
            Ok(v)
        } else {
            Err(anyhow!("Cannot cast {value:?} to bool."))
        }
    }
}

impl ConstantDefault for bool {
    fn constant_default() -> &'static Self {
        &CONSTANT_DEFAULT_BOOL
    }
}

impl_value_traits!(
    i64 => Integer / 0,
    f64 => Float / 0.0,
    String => String / String::new(),
    VecString => StringVec / Vec::new()
);

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

    // --- TryFrom<&Value> tests ---
    #[test]
    fn try_from_bool_success() {
        let val = Value::Boolean(true);
        let result: Result<&bool> = (&val).try_into();
        assert_eq!(result.unwrap(), &true);
    }

    #[test]
    fn try_from_bool_failure() {
        let val = Value::Integer(42);
        let result: Result<&bool> = (&val).try_into();
        assert!(result.is_err());
    }

    #[test]
    fn try_from_i64_success() {
        let val = Value::Integer(42);
        let result: Result<&i64> = (&val).try_into();
        assert_eq!(result.unwrap(), &42);
    }

    #[test]
    fn try_from_i64_failure() {
        let val = Value::Float(std::f64::consts::PI);
        let result: Result<&i64> = (&val).try_into();
        assert!(result.is_err());
    }

    #[test]
    fn try_from_f64_success() {
        let val = Value::Float(std::f64::consts::PI);
        let result: Result<&f64> = (&val).try_into();
        assert_eq!(result.unwrap(), &std::f64::consts::PI);
    }

    #[test]
    fn try_from_f64_failure() {
        let val = Value::String("hello".to_string());
        let result: Result<&f64> = (&val).try_into();
        assert!(result.is_err());
    }

    #[test]
    fn try_from_string_success() {
        let val = Value::String("hello".to_string());
        let result: Result<&String> = (&val).try_into();
        assert_eq!(result.unwrap(), "hello");
    }

    #[test]
    fn try_from_string_failure() {
        let val = Value::Boolean(false);
        let result: Result<&String> = (&val).try_into();
        assert!(result.is_err());
    }

    #[test]
    fn try_from_vec_string_success() {
        let val = Value::StringVec(vec!["a".to_string(), "b".to_string()]);
        let result: Result<&Vec<String>> = (&val).try_into();
        assert!(result.is_ok());
        let vec_ref = result.unwrap();
        assert_eq!(vec_ref.len(), 2);
        assert_eq!(vec_ref[0], "a");
    }

    #[test]
    fn try_from_vec_string_failure() {
        let val = Value::Integer(123);
        let result: Result<&Vec<String>> = (&val).try_into();
        assert!(result.is_err());
    }

    // --- ConstantDefault tests ---
    #[test]
    fn constant_default_bool() {
        assert!(!*bool::constant_default());
    }

    #[test]
    fn constant_default_i64() {
        assert_eq!(*i64::constant_default(), 0);
    }

    #[test]
    fn constant_default_f64() {
        assert_eq!(*f64::constant_default(), 0.0);
    }

    #[test]
    fn constant_default_string() {
        assert_eq!(*String::constant_default(), "");
    }

    #[test]
    fn constant_default_vec_string() {
        assert!(Vec::<String>::constant_default().is_empty());
    }
}
