// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::sync::Arc;

use super::*;
use super::super::{server::Server, mock_io::MockReader};

use crate::protocol::discovery::GameId;
use crate::utils::max_len::MaxLenArcStr;

#[test]
fn play_ser() {
	let data = Request::Play(PlayRequest {
		game_id: GameId::try_from(Arc::from("test")).unwrap(),
		spectate: false,
		name: MaxLenArcStr::try_from(Arc::from("Player")).unwrap(),
		colour: [255, 0, 0].into(),
		extension: Box::from([]),
	}).serialise().unwrap().0;

	assert_eq!(&data, &[b's', b'e', b'R', b'p', 20, 0, 5, 17, 4, b't', b'e', b's', b't', 0, 6, b'P', b'l', b'a', b'y', b'e', b'r', 255, 0, 0, 0]);
}

#[test]
fn info_ser() {
	let data = Request::Info.serialise().unwrap().0;
	assert_eq!(&data, &[b's', b'e', b'R', b'p', 3, 1, 0, 0]);
}

#[test]
fn discover_ser() {
	let data = Request::Discover.serialise().unwrap().0;
	assert_eq!(&data, &[b's', b'e', b'R', b'p', 3, 2, 0, 0]);
}

#[tokio::test]
async fn property() {
	requests(async |req| {
		let (data, _request_len) = req.serialise().unwrap();
		let (req2, subrequest_index) = Server::recv_with(&mut MockReader(&data)).await.unwrap();
		assert_eq!(req, req2);
		assert_eq!(subrequest_index, Vu30::ZERO);
	}).await;
}

async fn requests(f: impl AsyncFn(Request)) {
	for _ in 0..1000 {
		f(Request::Play(PlayRequest {
			game_id: GameId::try_from(Arc::from(random_str().as_ref())).unwrap(),
			spectate: rand::random::<bool>(),
			name: random_name(),
			colour: random_colour(),
			extension: random_bytes(),
		})).await;
	}

	f(Request::Info).await;
	f(Request::Discover).await;

	for _ in 0..1000 {
		f(Request::Publish(PublishRequest::Init(rand::random::<u16>(), rand::random::<bool>().then(random_hostname), Key::try_new().unwrap()))).await;
		f(Request::Publish(PublishRequest::Subscribe(Key::try_new().unwrap()))).await;
	}
}

fn random_str() -> Box<str> {
	if rand::random::<bool>() { return Box::from(""); }

	let mut s = String::new();
	for _ in 0..rand::random::<u32>() % 15 {
		s.push(rand::random::<char>());
	}
	s.into_boxed_str()
}

fn random_name() -> PlayerName {
	MaxLenArcStr::try_from(Arc::from(random_str().as_ref())).unwrap()
}

fn random_colour() -> Colour {
	[rand::random::<u8>(), rand::random::<u8>(), rand::random::<u8>()].into()
}

fn random_bytes() -> Box<[u8]> {
	if rand::random::<bool>() { return Box::from([]); }

	let mut s = Vec::new();
	for _ in 0..rand::random::<u32>() % 15 {
		s.push(rand::random::<u8>());
	}
	s.into_boxed_slice()
}

fn random_hostname() -> Hostname {
	let mut s = String::new();
	for _ in 0..rand::random::<u32>() % 5 {
		s.push_str(&random_label());
		s.push('.');
	}
	s.push_str(&random_label());
	Hostname::try_from(Arc::from(s.as_str())).unwrap()
}

fn random_label() -> Box<str> {
	let mut s = String::new();
	for _ in 0..=rand::random::<u32>() % 10 {
		s.push(char::try_from('a' as u32 + rand::random::<u32>() % 26).unwrap());
	}
	s.into_boxed_str()
}
