use std::borrow::Cow;
use std::fmt;
pub use http::header::{
	AsHeaderName, HeaderName, HeaderValue, IntoHeaderName, InvalidHeaderValue,
};
#[cfg(feature = "json")]
pub use serde_json::Error as JsonError;
#[derive(Debug, Clone)]
pub struct HeaderValues(http::HeaderMap<HeaderValue>);
impl HeaderValues {
	pub fn new() -> Self {
		Self(http::HeaderMap::new())
	}
	pub fn from_inner(inner: http::HeaderMap<HeaderValue>) -> Self {
		Self(inner)
	}
	pub fn insert<K, V>(&mut self, key: K, val: V) -> Option<HeaderValue>
	where
		K: IntoHeaderName,
		V: TryInto<HeaderValue>,
		V::Error: fmt::Debug,
	{
		let val = val.try_into().expect("invalid HeaderValue");
		self.0.insert(key, val)
	}
	pub fn try_insert<K, V>(
		&mut self,
		key: K,
		val: V,
	) -> Result<Option<HeaderValue>, InvalidHeaderValue>
	where
		K: IntoHeaderName,
		V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
	{
		Ok(self.0.insert(key, val.try_into()?))
	}
	pub fn encode_value<K, V>(&mut self, key: K, val: V) -> Option<HeaderValue>
	where
		K: IntoHeaderName,
		V: IntoEncodedHeaderValue,
	{
		let val = val.into_encoded_header_value();
		self.0.insert(key, val)
	}
	#[cfg(feature = "json")]
	#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
	pub fn serialize_value<K, V>(
		&mut self,
		key: K,
		val: &V,
	) -> Result<Option<HeaderValue>, JsonError>
	where
		K: IntoHeaderName,
		V: serde::Serialize + ?Sized,
	{
		let v = serde_json::to_string(val)?;
		Ok(self.encode_value(key, v))
	}
	pub fn get<K>(&self, key: K) -> Option<&HeaderValue>
	where
		K: AsHeaderName,
	{
		self.0.get(key)
	}
	pub fn get_mut<K>(&mut self, key: K) -> Option<&mut HeaderValue>
	where
		K: AsHeaderName,
	{
		self.0.get_mut(key)
	}
	pub fn get_str<K>(&self, key: K) -> Option<&str>
	where
		K: AsHeaderName,
	{
		self.get(key).and_then(|v| v.to_str().ok())
	}
	pub fn decode_value<K>(
		&self,
		key: K,
	) -> Option<Result<Cow<'_, str>, std::str::Utf8Error>>
	where
		K: AsHeaderName,
	{
		self.get(key).map(|v| {
			percent_encoding::percent_decode(v.as_bytes()).decode_utf8()
		})
	}
	#[cfg(feature = "json")]
	#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
	pub fn deserialize_value<K, D>(
		&self,
		key: K,
	) -> Option<Result<D, serde_json::Error>>
	where
		K: AsHeaderName,
		D: serde::de::DeserializeOwned,
	{
		self.get(key).map(|v| {
			let s = percent_encoding::percent_decode(v.as_bytes())
				.decode_utf8_lossy();
			serde_json::from_str(s.as_ref())
		})
	}
	pub fn into_inner(self) -> http::HeaderMap<HeaderValue> {
		self.0
	}
}
impl Default for HeaderValues {
	fn default() -> Self {
		Self::new()
	}
}
fn encode_to_header_value(s: impl AsRef<[u8]>) -> HeaderValue {
	let s: String = percent_encoding::percent_encode(
		s.as_ref(),
		percent_encoding::CONTROLS,
	)
	.collect();
	let b: bytes::Bytes = s.into();
	HeaderValue::from_maybe_shared(b).unwrap()
}
pub trait IntoEncodedHeaderValue {
	fn into_encoded_header_value(self) -> HeaderValue;
}
macro_rules! impl_into_header_value {
	($(
		$s:ty, $self:ident => $ex:expr
	),*) => ($(
		impl IntoEncodedHeaderValue for $s {
			#[inline]
			fn into_encoded_header_value($self) -> HeaderValue { $ex }
		}
	)*);
	(REF, $(
		$s:ty, $self:ident => $ex:expr
	),*) => ($(
		impl<'a> IntoEncodedHeaderValue for &'a $s {
			#[inline]
			fn into_encoded_header_value($self) -> HeaderValue { $ex }
		}
	)*);
}
impl_into_header_value! {
	HeaderName, self => self.into(),
	HeaderValue, self => self,
	i16, self => self.into(),
	i32, self => self.into(),
	i64, self => self.into(),
	isize, self => self.into(),
	u16, self => self.into(),
	u32, self => self.into(),
	u64, self => self.into(),
	usize, self => self.into(),
	String, self => encode_to_header_value(self),
	Vec<u8>, self => encode_to_header_value(self)
}
impl_into_header_value! { REF,
	HeaderValue, self => self.clone(),
	[u8], self => encode_to_header_value(self),
	str, self => encode_to_header_value(self)
}
#[cfg(test)]
mod tests {
	#![allow(unused_imports)]
	use super::*;
	use serde::{Deserialize, Serialize};
	#[test]
	fn test_encdec() {
		let mut values = HeaderValues::new();
		values.encode_value("Rocket", "🚀 Rocket");
		let s = values.get_str("Rocket").unwrap();
		assert_eq!(s, "%F0%9F%9A%80 Rocket");
		let s = values.decode_value("Rocket").unwrap().unwrap();
		assert_eq!(s, "🚀 Rocket");
	}
	#[cfg(feature = "json")]
	#[test]
	fn test_serde() {
		#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
		struct Value {
			text: String,
			number: usize,
		}
		let mut values = HeaderValues::new();
		let val = Value {
			text: "🚀 Rocket".into(),
			number: 42,
		};
		values.serialize_value("Value", &val).unwrap();
		let s = values.get_str("Value").unwrap();
		assert_eq!(s, "{\"text\":\"%F0%9F%9A%80 Rocket\",\"number\":42}");
		let n_val: Value = values.deserialize_value("Value").unwrap().unwrap();
		assert_eq!(n_val, val);
	}
}