use std::fmt;

#[derive(Clone, PartialEq, Eq, Debug, Hash, Default)]
pub enum CustomUnitAttribute {
	#[default]
	None,
	AllowLongPrefix,
	AllowShortPrefix,
	IsLongPrefix,
	Alias,
}

struct CustomUnitAttributeVisitor;

impl<'de> serde::de::Visitor<'de> for CustomUnitAttributeVisitor {
	type Value = CustomUnitAttribute;

	fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
		formatter.write_str(
			"`none`, `allow-long-prefix`, `allow-short-prefix`, `is-long-prefix` or `alias`",
		)
	}

	fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
		Ok(match v {
			"none" => CustomUnitAttribute::None,
			"allow-long-prefix" => CustomUnitAttribute::AllowLongPrefix,
			"allow-short-prefix" => CustomUnitAttribute::AllowShortPrefix,
			"is-long-prefix" => CustomUnitAttribute::IsLongPrefix,
			"alias" => CustomUnitAttribute::Alias,
			unknown => {
				return Err(serde::de::Error::unknown_variant(
					unknown,
					&[
						"none",
						"allow-long-prefix",
						"allow-short-prefix",
						"is-long-prefix",
						"alias",
					],
				))
			}
		})
	}
}

impl<'de> serde::Deserialize<'de> for CustomUnitAttribute {
	fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
		deserializer.deserialize_str(CustomUnitAttributeVisitor)
	}
}

impl CustomUnitAttribute {
	pub fn to_fend_core(&self) -> fend_core::CustomUnitAttribute {
		match self {
			CustomUnitAttribute::None => fend_core::CustomUnitAttribute::None,
			CustomUnitAttribute::AllowLongPrefix => fend_core::CustomUnitAttribute::AllowLongPrefix,
			CustomUnitAttribute::AllowShortPrefix => {
				fend_core::CustomUnitAttribute::AllowShortPrefix
			}
			CustomUnitAttribute::IsLongPrefix => fend_core::CustomUnitAttribute::IsLongPrefix,
			CustomUnitAttribute::Alias => fend_core::CustomUnitAttribute::Alias,
		}
	}
}

#[derive(Debug, Eq, PartialEq)]
pub struct CustomUnitDefinition {
	pub singular: String,
	pub plural: String,
	pub definition: String,
	pub attribute: CustomUnitAttribute,
}

impl<'de> serde::Deserialize<'de> for CustomUnitDefinition {
	fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
		const FIELDS: &[&str] = &["singular", "plural", "definition", "attribute"];

		struct CustomUnitDefinitionVisitor;

		impl<'de> serde::de::Visitor<'de> for CustomUnitDefinitionVisitor {
			type Value = CustomUnitDefinition;

			fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
				formatter.write_str("a custom unit definition, with properties `singular`, `plural`, `definition` and `attribute`")
			}

			fn visit_map<V: serde::de::MapAccess<'de>>(
				self,
				mut map: V,
			) -> Result<CustomUnitDefinition, V::Error> {
				let mut result = CustomUnitDefinition {
					attribute: CustomUnitAttribute::None,
					definition: String::new(),
					singular: String::new(),
					plural: String::new(),
				};
				let mut seen_singular = false;
				let mut seen_plural = false;
				let mut seen_definition = false;
				let mut seen_attribute = false;
				while let Some(key) = map.next_key::<String>()? {
					match key.as_str() {
						"singular" => {
							if seen_singular {
								return Err(serde::de::Error::duplicate_field("singular"));
							}
							result.singular = map.next_value()?;
							seen_singular = true;
						}
						"plural" => {
							if seen_plural {
								return Err(serde::de::Error::duplicate_field("plural"));
							}
							result.plural = map.next_value()?;
							if result.plural.is_empty() {
								return Err(serde::de::Error::invalid_value(
									serde::de::Unexpected::Str(&result.plural),
									&"a non-empty string describing the plural form of this unit",
								));
							}
							seen_plural = true;
						}
						"definition" => {
							if seen_definition {
								return Err(serde::de::Error::duplicate_field("definition"));
							}
							result.definition = map.next_value()?;
							if result.definition.is_empty() {
								return Err(serde::de::Error::invalid_value(
									serde::de::Unexpected::Str(&result.definition),
									&"a non-empty string that contains the definition of this unit",
								));
							}
							seen_definition = true;
						}
						"attribute" => {
							if seen_attribute {
								return Err(serde::de::Error::duplicate_field("attribute"));
							}
							result.attribute = map.next_value()?;
							seen_attribute = true;
						}
						unknown_key => {
							map.next_value::<toml::Value>()?;
							return Err(serde::de::Error::unknown_field(unknown_key, FIELDS));
						}
					}
				}
				if result.singular.is_empty() {
					return Err(serde::de::Error::missing_field("singular"));
				}
				if result.definition.is_empty() {
					return Err(serde::de::Error::missing_field("definition"));
				}
				Ok(result)
			}
		}

		deserializer.deserialize_struct("CustomUnitDefinition", FIELDS, CustomUnitDefinitionVisitor)
	}
}
