use std::{
	collections::HashMap,
	fmt::{self, Display, Formatter},
	io::{self, Error, ErrorKind},
	path::Path,
	sync::{LazyLock, Mutex},
};

const FONTAWESOME_DIR: &str = "/usr/share/nodejs/@fortawesome/fontawesome-free/svgs";

static CACHE: LazyLock<Mutex<HashMap<(Type, String), &'static str>>> = LazyLock::new(|| Mutex::new(HashMap::new()));

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub(crate) enum Type {
	Brands,
	Regular,
	Solid,
}

impl Type {
	fn as_str(self) -> &'static str {
		match self {
			Self::Brands => "brands",
			Self::Regular => "regular",
			Self::Solid => "solid",
		}
	}
}

impl Display for Type {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		write!(f, "{}", self.as_str())
	}
}

impl std::str::FromStr for Type {
	type Err = ();

	fn from_str(value: &str) -> Result<Self, Self::Err> {
		match value {
			"brands" => Ok(Self::Brands),
			"regular" => Ok(Self::Regular),
			"solid" => Ok(Self::Solid),
			_ => Err(()),
		}
	}
}

pub(crate) fn svg(type_: Type, name: &str) -> io::Result<&'static str> {
	if name.is_empty() || !name.bytes().all(|byte| byte.is_ascii_alphanumeric() || byte == b'-') {
		return Err(Error::new(ErrorKind::InvalidInput, "invalid FontAwesome icon name"));
	}

	let key = (type_, name.to_owned());
	let mut cache = CACHE.lock().expect("FontAwesome cache poisoned");
	if let Some(&svg) = cache.get(&key) {
		return Ok(svg);
	}

	let path = Path::new(FONTAWESOME_DIR).join(type_.as_str()).join(format!("{name}.svg"));
	let svg = std::fs::read_to_string(&path)
		.map_err(|e| Error::new(e.kind(), format!("failed to read `{}`: {e:?}", path.display())))?;
	let svg: &'static str = Box::leak(svg.into_boxed_str());
	cache.insert(key, svg);
	Ok(svg)
}
