use crate::{
    datafile::datafile::DataFile,
    targeting::{
        conditions::{
            visitor_scope_condition::{VisitScope, VisitorScopeCondition},
            TargetingCondition,
        },
        models::ConversionsCPModel,
    },
};

pub(crate) struct ConversionCondition {
    goal_id: Option<u32>,
    visitor_scope_condition: VisitorScopeCondition,
}

impl ConversionCondition {
    pub fn from_json(model: ConversionsCPModel) -> Option<Self> {
        return Some(Self {
            goal_id: model.goal_id,
            visitor_scope_condition: VisitorScopeCondition::new(
                model.visit_params.visit_scope,
                model.visit_params.visit_count,
                VisitScope::Visitor,
            ),
        });
    }
}

impl TargetingCondition for ConversionCondition {
    fn check(&self, data: &crate::targeting::TargetingData<impl DataFile>) -> Option<bool> {
        let threshold = self.visitor_scope_condition.assignment_threshold(data);
        Some(
            data.visitor_data
                .conversions
                .iter()
                .any(|c| self.goal_id.map_or(true, |goal_id| goal_id == c.goal_id) && c.assignment_time >= threshold),
        )
    }
}

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

    use rstest::rstest;

    fn new_conversion(goal_id: u32, assignment_ts: u64) -> Conversion {
        Conversion::new_with_opts_time(goal_id, crate::data::ConversionOpts::new(), millis_to_time(assignment_ts))
    }

    #[rstest]
    #[case(1, None, false)]
    #[case(1, Some(vec![new_conversion(1000, 500)]), true)]
    #[case(1, Some(vec![new_conversion(1000, 100)]), true)]
    #[case(1, Some(vec![new_conversion(1000, 99)]), false)]
    #[case(1, Some(vec![new_conversion(999, 500)]), false)]
    #[case(2, Some(vec![new_conversion(999, 900)]), true)]
    #[case(2, Some(vec![new_conversion(999, 700)]), false)]
    #[case(2, Some(vec![]), false)]
    fn test_check(#[case] file_index: i32, #[case] conversions: Option<Vec<Conversion>>, #[case] expected: bool) {
        let datafile = MockDataFile::default();
        let visitor = Visitor::new();
        visitor.add_data(std::iter::once(BaseData::VisitorVisits(new_visitor_visits())), true);
        if let Some(conversions) = conversions {
            visitor.add_data(conversions.into_iter().map(BaseData::Conversion), true);
        }
        assert_targeting(
            &format!("resources/conditions/mock_segment_conversion{file_index}.json"),
            &TargetingDataBuilder::new().build(&datafile, &visitor.rdata()),
            expected,
        );
    }
}
