use std::sync::Arc;

use crate::datafile::variable::VariableType;

#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Clone))]
pub struct Variable {
    pub key: Arc<str>,
    pub kind: Arc<str>,
    pub value: JsonValue,
}

#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub enum JsonValue {
    Boolean(bool),
    Number(f64),
    String(Arc<str>),
    Json(Arc<str>),
    Js(Arc<str>),
    Css(Arc<str>),
}

impl JsonValue {
    pub(crate) fn from_datafile_value(var_type: &VariableType, value: serde_json::Value) -> Self {
        match var_type {
            VariableType::Boolean => Self::Boolean(value.as_bool().unwrap_or(false)),
            VariableType::Number => Self::Number(value.as_f64().unwrap_or(f64::NAN)),
            VariableType::Json => Self::Json(Self::string_value(value)),
            VariableType::Js => Self::Js(Self::string_value(value)),
            VariableType::Css => Self::Css(Self::string_value(value)),
            _ => Self::String(Self::string_value(value)),
        }
    }

    fn string_value(value: serde_json::Value) -> Arc<str> {
        match value {
            serde_json::Value::String(value) => Arc::from(value),
            _ => Arc::from(""),
        }
    }
}

impl JsonValue {
    pub fn as_number(&self) -> Option<f64> {
        match self {
            Self::Number(n) => Some(*n),
            _ => None,
        }
    }

    pub fn as_bool(&self) -> Option<bool> {
        match self {
            Self::Boolean(b) => Some(*b),
            _ => None,
        }
    }

    pub fn as_str(&self) -> Option<&str> {
        match self {
            Self::String(s) | Self::Json(s) | Self::Js(s) | Self::Css(s) => Some(s.as_ref()),
            _ => None,
        }
    }
}
