use super::*;

#[test]
fn server_processing_greater_than_raw_does_not_underflow() {
    let base = Instant::now();
    let rtt = compute_rtt(
        &ClientTimestamp {
            mono: base,
            wall: SystemTime::now(),
        },
        &ClientTimestamp {
            mono: base + Duration::from_nanos(1),
            wall: SystemTime::now(),
        },
        &TimestampFields {
            recv_mono: Some(0),
            send_mono: Some(1_000_000_000),
            ..Default::default()
        },
    );
    assert_eq!(rtt.adjusted, Some(SignedDuration::from_nanos(-999_999_999)));
    assert_eq!(rtt.effective, SignedDuration::from_nanos(-999_999_999));
}

#[test]
fn effective_rtt_uses_raw_when_adjusted_rtt_is_unavailable() {
    let base = Instant::now();
    let rtt = compute_rtt(
        &ClientTimestamp {
            mono: base,
            wall: SystemTime::now(),
        },
        &ClientTimestamp {
            mono: base + Duration::from_nanos(42),
            wall: SystemTime::now(),
        },
        &TimestampFields::default(),
    );

    assert_eq!(rtt.adjusted, None);
    assert_eq!(rtt.effective, SignedDuration::from_duration(rtt.raw));
}

#[test]
fn unix_epoch_ns_i64_rejects_out_of_range_wall_time() {
    let i64_max_ns = u64::try_from(i64::MAX).unwrap();
    let max = SystemTime::UNIX_EPOCH + Duration::from_nanos(i64_max_ns);
    let overflow = max + Duration::from_nanos(1);

    assert_eq!(unix_epoch_ns_i64(max), Some(i64::MAX));
    assert_eq!(unix_epoch_ns_i64(overflow), None);
}

#[test]
fn compute_one_way_omits_samples_when_client_wall_time_overflows_i64() {
    let i64_max_ns = u64::try_from(i64::MAX).unwrap();
    let overflow_wall =
        SystemTime::UNIX_EPOCH + Duration::from_nanos(i64_max_ns) + Duration::from_nanos(1);
    let sent_at = ClientTimestamp {
        mono: Instant::now(),
        wall: overflow_wall,
    };
    let received_at = ClientTimestamp {
        mono: sent_at.mono + Duration::from_millis(40),
        wall: overflow_wall + Duration::from_millis(40),
    };
    let ts = TimestampFields {
        recv_wall: Some(i64::MAX),
        send_wall: Some(i64::MAX),
        ..Default::default()
    };

    assert_eq!(compute_one_way(&sent_at, &received_at, &ts), None);
}

#[test]
fn compute_one_way_returns_available_direction_samples() {
    let sent_at = ClientTimestamp {
        mono: Instant::now(),
        wall: SystemTime::UNIX_EPOCH + Duration::from_secs(10),
    };
    let received_at = ClientTimestamp {
        mono: sent_at.mono + Duration::from_millis(40),
        wall: SystemTime::UNIX_EPOCH + Duration::from_secs(10) + Duration::from_millis(40),
    };
    let ts = TimestampFields {
        recv_wall: Some(10_000_000_000 + 15_000_000),
        send_wall: Some(10_000_000_000 + 25_000_000),
        ..Default::default()
    };

    let sample = compute_one_way(&sent_at, &received_at, &ts).unwrap();
    assert_eq!(
        sample.client_to_server,
        Some(SignedDuration::from_nanos(15_000_000))
    );
    assert_eq!(
        sample.server_to_client,
        Some(SignedDuration::from_nanos(15_000_000))
    );
}

#[test]
fn compute_one_way_preserves_negative_client_to_server_delay() {
    let sent_at = ClientTimestamp {
        mono: Instant::now(),
        wall: SystemTime::UNIX_EPOCH + Duration::from_secs(10),
    };
    let received_at = ClientTimestamp {
        mono: sent_at.mono + Duration::from_millis(40),
        wall: SystemTime::UNIX_EPOCH + Duration::from_secs(10) + Duration::from_millis(40),
    };
    let ts = TimestampFields {
        recv_wall: Some(10_000_000_000 - 5_000_000),
        send_wall: Some(10_000_000_000 + 25_000_000),
        ..Default::default()
    };

    let sample = compute_one_way(&sent_at, &received_at, &ts).unwrap();

    assert_eq!(
        sample.client_to_server,
        Some(SignedDuration::from_nanos(-5_000_000))
    );
    assert_eq!(
        sample.server_to_client,
        Some(SignedDuration::from_nanos(15_000_000))
    );
}

#[test]
fn compute_one_way_preserves_negative_server_to_client_delay() {
    let sent_at = ClientTimestamp {
        mono: Instant::now(),
        wall: SystemTime::UNIX_EPOCH + Duration::from_secs(10),
    };
    let received_at = ClientTimestamp {
        mono: sent_at.mono + Duration::from_millis(40),
        wall: SystemTime::UNIX_EPOCH + Duration::from_secs(10) + Duration::from_millis(40),
    };
    let ts = TimestampFields {
        recv_wall: Some(10_000_000_000 + 15_000_000),
        send_wall: Some(10_000_000_000 + 45_000_000),
        ..Default::default()
    };

    let sample = compute_one_way(&sent_at, &received_at, &ts).unwrap();

    assert_eq!(
        sample.client_to_server,
        Some(SignedDuration::from_nanos(15_000_000))
    );
    assert_eq!(
        sample.server_to_client,
        Some(SignedDuration::from_nanos(-5_000_000))
    );
}
