// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::{sync::Arc, fmt::{Display, Formatter, Result as FmtResult}};

use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(try_from = "Arc<str>")]
pub struct Hostname(Arc<str>);

impl Hostname {
	pub fn get(&self) -> &str {
		self.0.as_ref()
	}
}

/**
 * Performs some validation rules on hostnames for the purpose of security. The
 * length limit prevents them being arbitrarily long, with this 253 byte limit
 * being the actual maximum hostname length.
 *
 * The characters are restricted to what hostnames allow (internationalised
 * domain names are internally represented using Punycode, which consists of
 * these characters), but actually more restrictive because uppercase characters
 * aren't allowed at all.
 *
 * I'm doing this is to avoid spoofing an 'l' with an 'I' which look very
 * similar in some fonts (including Inter which is what this game uses). I could
 * instead normalise uppercase characters to lowercase to prevent this problem,
 * but I'm lazy and who actually uses uppercase in hostnames?
 *
 * Source for hostname rules: https://en.wikipedia.org/wiki/Hostname#Syntax
 */
impl TryFrom<Arc<str>> for Hostname {
	type Error = &'static str;

	fn try_from(s: Arc<str>) -> Result<Hostname, &'static str> {
		if s.len() > 253 {
			return Err("too long, expected at most 253 bytes");
		}

		for &b in s.as_bytes() {
			if !matches!(b, b'a'..=b'z' | b'0'..=b'9' | b'.' | b'-') {
				return Err("expected lowercase ASCII letters, digits, hyphens and dots");
			}
		}

		Ok(Hostname(s))
	}
}

impl Display for Hostname {
	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
		self.0.fmt(fmt)
	}
}
