use crate::{
    datafile::datafile::DataFile,
    targeting::{conditions::TargetingCondition, models::VersionCPModel, NumberTargetingOperator, TargetingData},
    utils::version::SemVer,
};

pub(crate) struct VersionCondition {
    version: SemVer,
    version_match_type: NumberTargetingOperator,
}

impl VersionCondition {
    pub fn from_json(model: VersionCPModel) -> Option<Self> {
        Some(Self {
            version: SemVer::parse(&model.version)?,
            version_match_type: NumberTargetingOperator::from_str(&model.version_match_type)?,
        })
    }
}

impl TargetingCondition for VersionCondition {
    fn check(&self, data: &TargetingData<impl DataFile>) -> Option<bool> {
        let Some(app_version) = data.visitor_data.application_version.r().get() else {
            return Some(false);
        };
        Some(match self.version_match_type {
            NumberTargetingOperator::Equal => *app_version == self.version,
            NumberTargetingOperator::Greater => *app_version > self.version,
            NumberTargetingOperator::Lower => *app_version < self.version,
        })
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        data::{application_version::ApplicationVersion, BaseData},
        datafile::datafile_test_utils::MockDataFile,
        storage::visitor::Visitor,
        targeting::TargetingDataBuilder,
        utils::test_utils::assert_targeting,
    };

    use rstest::rstest;

    #[rstest]
    // Equal
    #[case("1.2.3", true)]
    // Greater
    #[case("5.0.3", true)]
    #[case("5.0.2", false)]
    #[case("5.0.1", false)]
    // Lower
    #[case("1.0.0", true)]
    #[case("1.0", true)]
    #[case("1", true)]
    #[case("1.0.1", false)]
    #[case("1.0.2", false)]
    // Wrong language
    #[case("wrong version", false)]
    fn test_application_version(#[case] version: &str, #[case] expected: bool) {
        let datafile = MockDataFile::default();
        let visitor = Visitor::new();
        let app_version = ApplicationVersion::new(version.to_owned());
        visitor.add_data([BaseData::ApplicationVersion(app_version)].into_iter(), true);
        assert_targeting(
            "resources/conditions/mock_segment_app_version.json",
            &TargetingDataBuilder::new().build(&datafile, &visitor.rdata()),
            expected,
        );
    }
}
