use std::time::{Duration, SystemTime};

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

pub(crate) struct TimeElapsedSinceVisitCondition {
    number_condition: NumberCondition<u64>,
    is_first_visit: bool,
}

impl TimeElapsedSinceVisitCondition {
    pub fn from_json(is_first_visit: bool, model: FirstLastVisitCPModel) -> Option<Self> {
        return Some(Self {
            is_first_visit,
            number_condition: NumberCondition::new(model.count_in_millis, model.match_type)?,
        });
    }
}

impl TargetingCondition for TimeElapsedSinceVisitCondition {
    fn check(&self, data: &TargetingData<impl DataFile>) -> Option<bool> {
        let visitor_visits = data.visitor_data.visitor_visits.r().get()?;
        let prev_visits = visitor_visits.prev_visits();
        let prev_visit_count = prev_visits.len();
        if prev_visit_count == 0 {
            return Some(false);
        }
        let now = SystemTime::now();
        let visit = &prev_visits[if self.is_first_visit { prev_visit_count - 1 } else { 0 }];
        let duration_since_visit = now.duration_since(visit.time_started).unwrap_or(Duration::ZERO);
        Some(self.number_condition.check(duration_since_visit.as_millis() as u64))
    }
}

#[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,
    };

    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_time_offset(offset: u64) -> Visit {
        let time_started = SystemTime::now() - Duration::from_millis(offset);
        return Visit::new(time_started, time_started);
    }

    #[rstest]
    // FIRST
    #[case("first", None, false)]
    #[case("first", Some(new_vv(vec![])), false)]
    #[case("first", Some(new_vv(vec![visit_from_time_offset(1500)])), false)]
    #[case("first", Some(new_vv(vec![visit_from_time_offset(900)])), true)]
    #[case("first", Some(new_vv(vec![visit_from_time_offset(900), visit_from_time_offset(1500)])), false)]
    #[case("first", Some(new_vv(vec![visit_from_time_offset(1500), visit_from_time_offset(900)])), true)]
    #[case("first", Some(new_vv(vec![visit_from_time_offset(3100)])), true)]
    #[case("first", Some(new_vv(vec![visit_from_time_offset(2000), visit_from_time_offset(3500)])), true)]
    #[case("first", Some(new_vv(vec![visit_from_time_offset(3500), visit_from_time_offset(2000)])), false)]
    // LAST
    #[case("last", None, false)]
    #[case("last", Some(new_vv(vec![])), false)]
    #[case("last", Some(new_vv(vec![visit_from_time_offset(1500)])), false)]
    #[case("last", Some(new_vv(vec![visit_from_time_offset(900)])), true)]
    #[case("last", Some(new_vv(vec![visit_from_time_offset(900), visit_from_time_offset(1500)])), true)]
    #[case("last", Some(new_vv(vec![visit_from_time_offset(1500), visit_from_time_offset(900)])), false)]
    #[case("last", Some(new_vv(vec![visit_from_time_offset(3100)])), true)]
    #[case("last", Some(new_vv(vec![visit_from_time_offset(2000), visit_from_time_offset(3500)])), false)]
    #[case("last", Some(new_vv(vec![visit_from_time_offset(3500), visit_from_time_offset(2000)])), true)]
    fn test_check(#[case] file_suffix: &str, #[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(
            &format!("resources/conditions/mock_segment_visit_{file_suffix}.json"),
            &TargetingDataBuilder::new().build(&datafile, &visitor.rdata()),
            expected,
        );
    }
}
