use crate::{
    data::page_view_visit::PageViewVisit,
    datafile::datafile::DataFile,
    targeting::{
        conditions::{string_value_condition::StringValueCondition, TargetingCondition},
        models::PreviousPageCPModel,
        TargetingData,
    },
};

pub(crate) struct PreviousPageCondition {
    string_condition: StringValueCondition,
}

impl PreviousPageCondition {
    pub fn from_json(model: PreviousPageCPModel) -> Option<Self> {
        let string_condition = StringValueCondition::new(model.url, model.match_type)?;
        return Some(Self { string_condition });
    }
}

impl TargetingCondition for PreviousPageCondition {
    fn check(&self, data: &TargetingData<impl DataFile>) -> Option<bool> {
        let mut most_recent_visit: Option<&PageViewVisit> = None;
        let mut second_most_recent_visit: Option<&PageViewVisit> = None;
        for pvv in data.visitor_data.page_view_visits.r().iter() {
            if most_recent_visit.map_or(true, |mrv| pvv.last_ts() > mrv.last_ts()) {
                second_most_recent_visit = most_recent_visit;
                most_recent_visit = Some(pvv);
            } else if second_most_recent_visit.map_or(true, |smrv| pvv.last_ts() > smrv.last_ts()) {
                second_most_recent_visit = Some(pvv);
            }
        }
        return Some(second_most_recent_visit.map_or(false, |smrv| self.string_condition.check(&smrv.page_view().url)));
    }
}

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

    use rstest::rstest;

    const TEST_URL: &str = "https://www.fish-pie.com/";
    const SECOND_URL: &str = "https://url2.com";
    const THIRD_URL: &str = "https://url3.com";

    #[rstest]
    #[case(None, false)]
    #[case(Some(vec![]), false)]
    #[case(Some(vec![PageViewVisit::new(PageView::new_with_url(TEST_URL), 10, None)]), false)]
    #[case(Some(vec![
        PageViewVisit::new(PageView::new_with_url(SECOND_URL), 10, millis_to_time_opt(20)),
        PageViewVisit::new(PageView::new_with_url(TEST_URL), 10, millis_to_time_opt(30)),
    ]), false)]
    #[case(Some(vec![
        PageViewVisit::new(PageView::new_with_url(THIRD_URL), 10, millis_to_time_opt(20)),
        PageViewVisit::new(PageView::new_with_url(SECOND_URL), 10, millis_to_time_opt(30)),
    ]), false)]
    #[case(Some(vec![
        PageViewVisit::new(PageView::new_with_url(TEST_URL), 10, millis_to_time_opt(20)),
        PageViewVisit::new(PageView::new_with_url(SECOND_URL), 10, millis_to_time_opt(30)),
    ]), true)]
    #[case(Some(vec![
        PageViewVisit::new(PageView::new_with_url(THIRD_URL), 10, millis_to_time_opt(15)),
        PageViewVisit::new(PageView::new_with_url(SECOND_URL), 10, millis_to_time_opt(30)),
        PageViewVisit::new(PageView::new_with_url(TEST_URL), 10, millis_to_time_opt(20)),
    ]), true)]
    fn test_check(#[case] page_view_visits: Option<Vec<PageViewVisit>>, #[case] expected: bool) {
        let datafile = MockDataFile::default();
        let visitor = Visitor::new();
        if let Some(page_view_visits) = page_view_visits {
            visitor.add_data(page_view_visits.into_iter().map(|pvv| BaseData::PageViewVisit(pvv)), true);
        }
        assert_targeting(
            "resources/conditions/mock_segment_previous_page.json",
            &TargetingDataBuilder::new().build(&datafile, &visitor.rdata()),
            expected,
        );
    }
}
