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

pub(crate) struct SdkLanguageCondition {
    sdk_language: String,
    sdk_version: Option<SemVer>,
    version_match_type: NumberTargetingOperator,
}

impl SdkLanguageCondition {
    pub fn from_json(model: SdkLanguageCPModel) -> Option<Self> {
        return Some(Self {
            sdk_language: model.sdk_language,
            sdk_version: model.version.map_or(None, |version| Some(SemVer::parse(&version)?)),
            version_match_type: NumberTargetingOperator::from_str(&model.version_match_type)?,
        });
    }
}

impl TargetingCondition for SdkLanguageCondition {
    fn check(&self, data: &TargetingData<impl DataFile>) -> Option<bool> {
        if data.sdk_name != self.sdk_language {
            return Some(false);
        }
        self.sdk_version.as_ref().map_or(Some(true), |sdk_version| {
            Some(match self.version_match_type {
                NumberTargetingOperator::Equal => data.sdk_version == *sdk_version,
                NumberTargetingOperator::Greater => data.sdk_version > *sdk_version,
                NumberTargetingOperator::Lower => data.sdk_version < *sdk_version,
            })
        })
    }
}

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

    use rstest::rstest;

    #[rstest]
    // Equal
    #[case("GO", SemVer::new(1, 2, 3), true)]
    // Greater
    #[case("GO", SemVer::new(5, 0, 3), true)]
    #[case("GO", SemVer::new(5, 0, 2), false)]
    #[case("GO", SemVer::new(5, 0, 1), false)]
    // Lower
    #[case("GO", SemVer::new(1, 0, 0), true)]
    #[case("GO", SemVer::new(1, 0, 1), false)]
    #[case("GO", SemVer::new(1, 0, 2), false)]
    // Wrong language
    #[case("CSHARP", SemVer::new(1, 2, 3), false)]
    // Any version
    #[case("JAVA", SemVer::new(1, 2, 2), true)]
    fn test_check(#[case] language: &str, #[case] version: SemVer, #[case] expected: bool) {
        let datafile = MockDataFile::default();
        let visitor = Visitor::new();
        assert_targeting(
            "resources/conditions/mock_segment_sdk_language.json",
            &TargetingDataBuilder::new().sdk_name(language).sdk_version(version).build(&datafile, &visitor.rdata()),
            expected,
        );
    }
}
