use std::{collections::HashMap, fmt::Debug};

//~ #[cfg(test)]
// use crate::datafile::variable::MockVariable;
use crate::{datafile::variable::Variable, network::dto::VariationDto};

pub(crate) const OFF_VALUE: &str = "off";

pub(crate) trait Variation: Send + Sync + Debug {
    type Variable: Variable;

    fn new(dto: VariationDto) -> Self;
    fn key(&self) -> &str;
    fn name(&self) -> &str;
    fn variables(&self) -> &HashMap<String, Self::Variable>;

    #[cfg(test)]
    fn is_active(&self) -> bool;
}

#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) struct VariationImpl<V: Variable> {
    key: String,
    name: String,
    variables: HashMap<String, V>,
}

impl<V: Variable> Variation for VariationImpl<V> {
    type Variable = V;

    fn new(dto: VariationDto) -> Self {
        let variables =
            dto.variables.into_iter().map(V::new).map(|v| (v.key().to_string(), v)).collect::<HashMap<_, _>>();
        Self {
            key: dto.key,
            name: dto.name,
            variables,
        }
    }

    fn key(&self) -> &str {
        &self.key
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn variables(&self) -> &HashMap<String, Self::Variable> {
        &self.variables
    }

    #[cfg(test)]
    fn is_active(&self) -> bool {
        self.key != OFF_VALUE
    }
}

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

    use crate::datafile::datafile_test_utils::{make_variable_dto, make_variation_dto};
    use crate::datafile::variable::{VariableImpl, VariableType};

    #[rstest]
    #[case("on", true)]
    #[case("off", false)]
    fn test_is_active(#[case] variation_key: &str, #[case] expected: bool) {
        let dto = make_variation_dto(variation_key, vec![]);
        let variation: VariationImpl<VariableImpl> = VariationImpl::new(dto);

        assert_eq!(variation.is_active(), expected);
    }

    #[rstest]
    #[case("flagA", "BOOLEAN", json!(true))]
    #[case("numberA", "NUMBER", json!(42))]
    fn test_variables_are_mapped(#[case] key: &str, #[case] var_type: &str, #[case] value: serde_json::Value) {
        let var_dto = make_variable_dto(key, var_type, value.clone());
        let dto = make_variation_dto("on", vec![var_dto]);

        let variation: VariationImpl<VariableImpl> = VariationImpl::new(dto);
        // Variation
        assert_eq!(variation.key(), "on");

        // Variable
        let variables = variation.variables();
        let variable = variables.get(key).expect("variable should exist");

        assert_eq!(variable.key(), key);
        assert_eq!(variable.var_type(), &VariableType::from_str(var_type));
        assert_eq!(variable.value(), &value);
    }
}
