use std::{fmt, str::FromStr};

use http::HeaderName;
use schemars::{json_schema, JsonSchema};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HttpHeaderName(HeaderName);

impl From<HeaderName> for HttpHeaderName {
    fn from(header_name: HeaderName) -> Self {
        HttpHeaderName(header_name)
    }
}

impl HttpHeaderName {
    pub fn new(header_name: impl AsRef<str>) -> Result<Self, http::header::InvalidHeaderName> {
        HeaderName::from_str(header_name.as_ref()).map(HttpHeaderName)
    }

    pub fn get_header_ref(&self) -> &HeaderName {
        &self.0
    }
}

impl Ord for HttpHeaderName {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.get_header_ref()
            .as_str()
            .cmp(other.get_header_ref().as_str())
    }
}

impl PartialOrd for HttpHeaderName {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl From<&str> for HttpHeaderName {
    fn from(header_name: &str) -> Self {
        HttpHeaderName(HeaderName::from_str(header_name).unwrap())
    }
}

impl From<String> for HttpHeaderName {
    fn from(header_name: String) -> Self {
        HttpHeaderName(HeaderName::from_str(&header_name).unwrap())
    }
}

impl Serialize for HttpHeaderName {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.0.as_str())
    }
}

struct HeaderNameVisitor;

impl JsonSchema for HttpHeaderName {
    fn schema_name() -> std::borrow::Cow<'static, str> {
        "HttpHeaderName".into()
    }

    fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
        json_schema!({
            "type": "string",
            "description": "A valid HTTP header name, according to RFC 7230.",
            "pattern": "^[A-Za-z0-9!#$%&'*+\\-.^_`|~]+$"
        })
    }

    fn inline_schema() -> bool {
        true
    }
}

impl<'de> serde::de::Visitor<'de> for HeaderNameVisitor {
    type Value = HttpHeaderName;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("an HTTP header name string (e.g., \"Content-Type\")")
    }

    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        HeaderName::from_str(value)
            .map(HttpHeaderName)
            .map_err(serde::de::Error::custom)
    }

    fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        self.visit_str(value)
    }

    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        self.visit_str(&value)
    }
}

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