use crate::datafile::variation::Variation as InternalVariation;
use crate::types::Variable;
use crate::{
    datafile::{
        experiment::Experiment, variable::Variable as InternalVariable, variation::OFF_VALUE,
        variation_exposition::VariationByExposition,
    },
    evaluation::evaluated_experiment::EvaluatedExperiment,
    types::variable::JsonValue,
};

#[derive(Debug, Default)]
pub struct Variation {
    pub key: String,
    pub name: String,
    pub id: Option<u32>,
    pub experiment_id: Option<u32>,
    pub variables: Vec<Variable>,
}

impl Variation {
    pub fn is_active(&self) -> bool {
        self.key != OFF_VALUE
    }

    pub fn get_variable(&self, key: &str) -> Option<&Variable> {
        self.variables.iter().find(|variable| variable.key == key)
    }

    pub(crate) fn from_internal(
        variation: &impl InternalVariation,
        id: Option<u32>,
        experiment_id: Option<u32>,
    ) -> Variation {
        let variables = variation
            .variables()
            .values()
            .map(|v| Variable {
                key: v.key().to_owned(),
                kind: v.var_type().as_str().to_owned(),
                value: JsonValue::from_internal(v.var_type(), v.value()),
            })
            .collect();

        Variation {
            key: variation.key().to_owned(),
            name: variation.name().to_owned(),
            id,
            experiment_id,
            variables,
        }
    }

    pub(crate) fn from_evaluated_experiment_info(
        internal_variation: &impl InternalVariation,
        eval_exp: Option<&EvaluatedExperiment<impl VariationByExposition, impl Experiment>>,
    ) -> Self {
        let id = eval_exp.map(|e| e.var_by_exp.variation_id());
        let experiment_id = eval_exp.map(|e| e.experiment.id());

        Self::from_internal(internal_variation, id, experiment_id)
    }
}

#[cfg(test)]
impl PartialEq for Variation {
    fn eq(&self, other: &Self) -> bool {
        if (self.key != other.key)
            || (self.id != other.id)
            || (self.experiment_id != other.experiment_id)
            || (self.variables.len() != other.variables.len())
        {
            return false;
        }
        let mut self_ordered_vars = self.variables.clone();
        let mut other_ordered_vars = other.variables.clone();
        self_ordered_vars.sort_by(|a, b| a.key.cmp(&b.key));
        other_ordered_vars.sort_by(|a, b| a.key.cmp(&b.key));
        self_ordered_vars == other_ordered_vars
    }
}
