Documentation
use std::time::SystemTime;

use crate::peer::{
	data::{
		PINGS_UNTIL_UNREACHABLE,
		PINGS_UNTIL_SUSPICIOUS,
		REPING_DELAY_MAX
	},
	link::{
		self,
		LinkTickResult
	},
	status
};
use crate::message::PingOrPong;

use super::*;

fn new_link() -> link::Link {
	link::Link::new(dummy_address(0))
}

#[test_log::test(test)]
fn remaining() {

	assert_eq!(status::remaining(None, Duration::MAX), None);

	assert!(
		status::remaining(Some(SystemTime::now()), Duration::MAX).unwrap()
		<= Duration::MAX
	);

	let one_s = Duration::from_secs(1);
	let three_s = Duration::from_secs(3);
	let four_s = Duration::from_secs(4);
	let three_s_ago = SystemTime::now() - three_s;

	assert_eq!(
		status::remaining(Some(three_s_ago), one_s),
		None
	);

	assert!(
		status::remaining(Some(three_s_ago), four_s).unwrap()
		<= one_s
	);

	let in_three_s = SystemTime::now() + three_s;

	assert_eq!(
		status::remaining(Some(in_three_s), one_s),
		None
	);
}

#[test_log::test(test)]
fn tick() {
	let first_tick = new_link().tick();
	assert!(matches!(
		first_tick,
		LinkTickResult::PingDue(_)
	));

	let mut l = new_link();
	l.has_pnged(PingOrPong::Ping);
	let just_seen = l.tick();
	assert!(matches!(
		dbg!(just_seen),
		LinkTickResult::Ok(_)
	));


	let mut l1 = new_link();
	l1.has_pnged(PingOrPong::Ping);
	let has_just_pinged = l1.tick();

	let mut l2 = new_link();
	l2.has_pnged(PingOrPong::Pong);
	let has_just_ponged = l2.tick();

	assert_ne!(
		has_just_pinged,
		has_just_ponged,
	);
	let pinged_d = if let LinkTickResult::Ok(t) = has_just_pinged {t} else {
		panic!("has_just_pinged not Ok(t)");
	};
	let ponged_d = if let LinkTickResult::Ok(t) = has_just_ponged {t} else {
		panic!("has_just_ponged not Ok(t)");
	};
	assert!(
		pinged_d < ponged_d,
		"if peer has just ponged then we sent the last ping, so we will wait a \
		little longer to give them a chance to ping first this time.\n\
		The peers \"take turns\" pinging to make sure they do not ping at the \
		same time."
	);
}



#[test_log::test(test)]
fn tick_flaky() {

	let mut l3 = new_link();
	l3.state = link::LinkState::BeenPinged(PINGS_UNTIL_SUSPICIOUS);
	l3.pinged = Some(SystemTime::now() - REPING_DELAY_MAX);
	let now_suspicious = l3.tick();

	assert!(matches!(
		dbg!(now_suspicious),
		LinkTickResult::LinkSuspicious(_)
	));


	let mut l4 = new_link();
	l4.state = link::LinkState::Suspicious(PINGS_UNTIL_UNREACHABLE);
	l4.pinged = Some(SystemTime::now() - REPING_DELAY_MAX);
	let now_unreachable = l4.tick();

	assert!(matches!(
		dbg!(now_unreachable),
		LinkTickResult::LinkUnreachable
	));

	let mut l5 = new_link();
	l5.state = link::LinkState::BeenPinged(0);
	l5.pinged = Some(SystemTime::now() - REPING_DELAY_MAX);
	let still_ok = l5.tick();

	assert!(matches!(
		dbg!(still_ok),
		LinkTickResult::PingDue(_)
	));

	let mut l6 = new_link();
	l6.state = link::LinkState::Suspicious(PINGS_UNTIL_SUSPICIOUS);
	l6.pinged = Some(SystemTime::now() - REPING_DELAY_MAX);
	let still_suspicious = l6.tick();

	assert!(matches!(
		dbg!(still_suspicious),
		LinkTickResult::PingDue(_)
	));
}