use chrono::TimeDelta;
use std::assert_matches;

use super::{generate_time_events, test::date_time_span, v2x::TimeRestrictions};
use crate::generate::EventCollector;

#[test]
fn should_emit_no_events_before_start_time() {
    // The chargesession takes place before the `start_time`
    let cdr_time_range = date_time_span("2025-11-10", "12:02:00", "2025-11-10", "14:00:00");
    let mut events = EventCollector::from_time_range(&cdr_time_range);

    generate_time_events(
        &mut events,
        chrono_tz::Tz::Europe__Amsterdam,
        cdr_time_range,
        TimeRestrictions {
            start_time: Some("15:00".parse().unwrap()),
            ..TimeRestrictions::default()
        },
    );

    assert_matches!(events.into_inner().as_slice(), []);
}

#[test]
fn should_emit_no_events_finishes_at_start_time_precisely() {
    let cdr_time_range = date_time_span("2025-11-10", "12:02:00", "2025-11-10", "14:00:00");
    let mut events = EventCollector::from_time_range(&cdr_time_range);

    // The chargesession takes place before the `start_time`
    generate_time_events(
        &mut events,
        chrono_tz::Tz::Europe__Amsterdam,
        cdr_time_range,
        TimeRestrictions {
            start_time: Some("15:00".parse().unwrap()),
            ..TimeRestrictions::default()
        },
    );

    assert_matches!(events.into_inner().as_slice(), []);
}

#[test]
fn should_emit_one_event_precise_overlap_with_start_time() {
    // 14:00 UTC = 15:00 Amsterdam (UTC+1), so the session starts exactly at start_time.
    let cdr_time_range = date_time_span("2025-11-10", "14:00:00", "2025-11-10", "16:00:00");
    let mut events = EventCollector::from_time_range(&cdr_time_range);

    // The chargesession starts precisely when the `start_time` generates an event.
    generate_time_events(
        &mut events,
        chrono_tz::Tz::Europe__Amsterdam,
        cdr_time_range,
        TimeRestrictions {
            start_time: Some("15:00".parse().unwrap()),
            ..TimeRestrictions::default()
        },
    );

    let [event] = events.into_inner().try_into().unwrap();
    assert_eq!(event.duration_from_start, TimeDelta::zero());
}

#[test]
fn should_emit_one_event_hour_before_start_time() {
    // 13:00 UTC = 14:00 Amsterdam (UTC+1), so the session starts one hour before start_time.
    let cdr_time_range = date_time_span("2025-11-10", "13:00:00", "2025-11-10", "16:00:00");
    let mut events = EventCollector::from_time_range(&cdr_time_range);

    // The chargesession starts an hour before the `start_time` generates an event.
    generate_time_events(
        &mut events,
        chrono_tz::Tz::Europe__Amsterdam,
        cdr_time_range,
        TimeRestrictions {
            start_time: Some("15:00".parse().unwrap()),
            ..TimeRestrictions::default()
        },
    );

    let [event] = events.into_inner().try_into().unwrap();
    assert_eq!(event.duration_from_start, TimeDelta::hours(1));
}

#[test]
fn should_emit_one_event_almost_full_day() {
    // 14:00 UTC = 15:00 Amsterdam (UTC+1), so the session starts at start_time and
    // ends just before the next occurrence (13:59 UTC = 14:59 Amsterdam).
    let cdr_time_range = date_time_span("2025-11-10", "14:00:00", "2025-11-11", "13:59:00");
    let mut events = EventCollector::from_time_range(&cdr_time_range);

    // The chargesession last from precisely the `start_time` of one day
    // until just before the `start_time` of the next.
    generate_time_events(
        &mut events,
        chrono_tz::Tz::Europe__Amsterdam,
        cdr_time_range,
        TimeRestrictions {
            start_time: Some("15:00".parse().unwrap()),
            ..TimeRestrictions::default()
        },
    );

    let [event] = events.into_inner().try_into().unwrap();
    assert_eq!(event.duration_from_start, TimeDelta::zero());
}

#[test]
fn should_emit_two_events_full_day_precisely() {
    // 14:00 UTC = 15:00 Amsterdam (UTC+1), so the session starts and ends exactly at start_time.
    let cdr_time_range = date_time_span("2025-11-10", "14:00:00", "2025-11-11", "14:00:00");
    let mut events = EventCollector::from_time_range(&cdr_time_range);

    generate_time_events(
        &mut events,
        chrono_tz::Tz::Europe__Amsterdam,
        cdr_time_range,
        TimeRestrictions {
            start_time: Some("15:00".parse().unwrap()),
            ..TimeRestrictions::default()
        },
    );

    let [event_0, event_1] = events.into_inner().try_into().unwrap();
    assert_eq!(event_0.duration_from_start, TimeDelta::zero());
    assert_eq!(event_1.duration_from_start, TimeDelta::days(1));
}

#[test]
fn should_emit_two_events_full_day_with_hour_margin() {
    // 13:00 UTC = 14:00 Amsterdam (UTC+1), so the session starts one hour before start_time.
    let cdr_time_range = date_time_span("2025-11-10", "13:00:00", "2025-11-11", "16:00:00");
    let mut events = EventCollector::from_time_range(&cdr_time_range);

    generate_time_events(
        &mut events,
        chrono_tz::Tz::Europe__Amsterdam,
        cdr_time_range,
        TimeRestrictions {
            start_time: Some("15:00".parse().unwrap()),
            ..TimeRestrictions::default()
        },
    );

    let [event_0, event_1] = events.into_inner().try_into().unwrap();
    assert_eq!(event_0.duration_from_start, TimeDelta::hours(1));
    assert_eq!(
        event_1.duration_from_start,
        TimeDelta::days(1) + TimeDelta::hours(1)
    );
}
