use crate::{
    datafile::datafile::DataFile,
    targeting::{
        conditions::{number_condition::NumberCondition, TargetingCondition},
        models::SameDayVisitsCPModel,
        TargetingData,
    },
    utils::time_utils,
};

pub(crate) struct VisitNumberTodayCondition {
    number_condition: NumberCondition<u32>,
}

impl VisitNumberTodayCondition {
    pub fn from_json(model: SameDayVisitsCPModel) -> Option<Self> {
        let number_condition = NumberCondition::new(model.visit_count, model.match_type)?;
        return Some(Self { number_condition });
    }
}

impl TargetingCondition for VisitNumberTodayCondition {
    fn check(&self, data: &TargetingData<impl DataFile>) -> Option<bool> {
        let start_of_day = time_utils::start_of_day_time();
        let mut number_of_visits_today = data.visitor_data.visitor_visits.r().get().map_or(0, |visitor_visits| {
            visitor_visits.prev_visits().iter().take_while(|v| v.time_started >= start_of_day).count() as u32
        });
        if data.visitor_data.time_started() >= start_of_day {
            number_of_visits_today += 1;
        }
        Some(self.number_condition.check(number_of_visits_today))
    }
}

#[cfg(test)]
mod tests {
    use std::time::{Duration, SystemTime};

    use crate::{
        data::{
            visitor_visits::{Visit, VisitorVisits},
            BaseData,
        },
        datafile::datafile_test_utils::MockDataFile,
        storage::visitor::Visitor,
        targeting::TargetingDataBuilder,
        utils::{test_utils::assert_targeting, time_utils::millis_to_time},
    };

    use super::*;
    use rstest::rstest;

    fn new_vv(prev_visits: Vec<Visit>) -> VisitorVisits {
        return VisitorVisits::new(prev_visits.len() as u32, prev_visits, SystemTime::UNIX_EPOCH, Duration::ZERO);
    }

    fn visit_from_sod(offset: i64) -> Visit {
        let time_started = millis_to_time((time_utils::start_of_day_millis() as i64 + offset) as u64);
        return Visit::new(time_started, time_started);
    }

    #[rstest]
    // LOWER
    #[case(None, true)]
    #[case(Some(new_vv(vec![])), true)]
    #[case(Some(new_vv(vec![visit_from_sod(-1)])), true)]
    #[case(Some(new_vv(vec![visit_from_sod(0)])), false)]
    // EXACT
    #[case(Some(new_vv(vec![visit_from_sod(1), visit_from_sod(0)])), true)]
    #[case(Some(new_vv(vec![visit_from_sod(0), visit_from_sod(-1)])), false)]
    // GREATER
    #[case(Some(new_vv(vec![visit_from_sod(3), visit_from_sod(2), visit_from_sod(1)])), false)]
    #[case(Some(new_vv(vec![visit_from_sod(3), visit_from_sod(2), visit_from_sod(1), visit_from_sod(-1)])), false)]
    #[case(Some(new_vv(vec![visit_from_sod(3), visit_from_sod(2), visit_from_sod(1), visit_from_sod(0)])), true)]
    fn test_check(#[case] visitor_visits: Option<VisitorVisits>, #[case] expected: bool) {
        let datafile = MockDataFile::default();
        let visitor = Visitor::new();
        if let Some(visitor_visits) = visitor_visits {
            visitor.add_data([BaseData::VisitorVisits(visitor_visits)].into_iter(), true);
        }
        assert_targeting(
            "resources/conditions/mock_segment_visit_today.json",
            &TargetingDataBuilder::new().build(&datafile, &visitor.rdata()),
            expected,
        );
    }
}
