use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::str::FromStr;

use serde::de;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::TypeError;
use crate::checked::{CheckedPort, CheckedString, CheckedStringRule};

macro_rules! impl_text_net_type {
    ($type:ty, $name:literal, $parse:ident, $target:ty, $example:literal) => {
        impl $type {
            pub fn as_str(&self) -> &str {
                self.0.as_str()
            }

            pub fn parse_inner(&self) -> Result<$target, TypeError> {
                parse_net::<$target>(self.as_str(), $name, $example)
            }

            pub fn into_string(self) -> String {
                self.0.into_string()
            }
        }

        impl FromStr for $type {
            type Err = TypeError;

            fn from_str(value: &str) -> Result<Self, Self::Err> {
                Self::new(value)
            }
        }

        impl fmt::Display for $type {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                f.write_str(self.as_str())
            }
        }

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

        impl<'de> Deserialize<'de> for $type {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where
                D: Deserializer<'de>,
            {
                let value = String::deserialize(deserializer)?;
                Self::new(value).map_err(de::Error::custom)
            }
        }
    };
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
    feature = "wire",
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type), sqlx(transparent, no_pg_array))]
pub struct Port(CheckedPort);

impl Port {
    pub fn new(value: u16) -> Result<Self, TypeError> {
        CheckedPort::new(value).map(Self)
    }

    pub fn get(self) -> u16 {
        self.0.get()
    }
}

impl FromStr for Port {
    type Err = TypeError;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        let value = value.parse::<u16>().map_err(|error| {
            TypeError::new(
                "Port",
                format!("expected port in 1..=65535: {error}"),
                "provide a valid TCP or UDP port like 8080",
            )
        })?;
        Self::new(value)
    }
}

impl fmt::Display for Port {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.get().fmt(f)
    }
}

impl Serialize for Port {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_u16(self.get())
    }
}

impl<'de> Deserialize<'de> for Port {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = u16::deserialize(deserializer)?;
        Self::new(value).map_err(de::Error::custom)
    }
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
    feature = "wire",
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type), sqlx(transparent, no_pg_array))]
pub struct Host(CheckedString<HostRule>);

impl Host {
    pub fn new(value: impl Into<String>) -> Result<Self, TypeError> {
        CheckedString::new(value).map(Self)
    }
}

impl_text_net_type!(Host, "Host", parse_host, String, "api.internal");

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
    feature = "wire",
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type), sqlx(transparent, no_pg_array))]
pub struct IpAddress(CheckedString<IpAddressRule>);

impl IpAddress {
    pub fn new(value: impl Into<String>) -> Result<Self, TypeError> {
        CheckedString::new(value).map(Self)
    }
}

impl From<IpAddr> for IpAddress {
    fn from(value: IpAddr) -> Self {
        Self(CheckedString::from_valid(value.to_string()))
    }
}

impl_text_net_type!(IpAddress, "IpAddress", parse_ip, IpAddr, "127.0.0.1");

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
    feature = "wire",
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type), sqlx(transparent, no_pg_array))]
pub struct Ipv4Address(CheckedString<Ipv4AddressRule>);

impl Ipv4Address {
    pub fn new(value: impl Into<String>) -> Result<Self, TypeError> {
        CheckedString::new(value).map(Self)
    }
}

impl From<Ipv4Addr> for Ipv4Address {
    fn from(value: Ipv4Addr) -> Self {
        Self(CheckedString::from_valid(value.to_string()))
    }
}

impl_text_net_type!(
    Ipv4Address,
    "Ipv4Address",
    parse_ipv4,
    Ipv4Addr,
    "127.0.0.1"
);

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
    feature = "wire",
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type), sqlx(transparent, no_pg_array))]
pub struct Ipv6Address(CheckedString<Ipv6AddressRule>);

impl Ipv6Address {
    pub fn new(value: impl Into<String>) -> Result<Self, TypeError> {
        CheckedString::new(value).map(Self)
    }
}

impl From<Ipv6Addr> for Ipv6Address {
    fn from(value: Ipv6Addr) -> Self {
        Self(CheckedString::from_valid(value.to_string()))
    }
}

impl_text_net_type!(Ipv6Address, "Ipv6Address", parse_ipv6, Ipv6Addr, "::1");

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
    feature = "wire",
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type), sqlx(transparent, no_pg_array))]
pub struct SocketAddress(CheckedString<SocketAddressRule>);

impl SocketAddress {
    pub fn new(value: impl Into<String>) -> Result<Self, TypeError> {
        CheckedString::new(value).map(Self)
    }
}

impl From<SocketAddr> for SocketAddress {
    fn from(value: SocketAddr) -> Self {
        Self(CheckedString::from_valid(value.to_string()))
    }
}

impl_text_net_type!(
    SocketAddress,
    "SocketAddress",
    parse_socket,
    SocketAddr,
    "127.0.0.1:8080"
);

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct HostRule;

impl CheckedStringRule for HostRule {
    fn check(value: String) -> Result<String, TypeError> {
        validate_host(&value)?;
        Ok(value)
    }
}

macro_rules! impl_net_rule {
    ($rule:ident, $target:ty, $name:literal, $example:literal) => {
        #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
        struct $rule;

        impl CheckedStringRule for $rule {
            fn check(value: String) -> Result<String, TypeError> {
                parse_net::<$target>(&value, $name, $example)?;
                Ok(value)
            }
        }
    };
}

impl_net_rule!(IpAddressRule, IpAddr, "IpAddress", "127.0.0.1");
impl_net_rule!(Ipv4AddressRule, Ipv4Addr, "Ipv4Address", "127.0.0.1");
impl_net_rule!(Ipv6AddressRule, Ipv6Addr, "Ipv6Address", "::1");
impl_net_rule!(
    SocketAddressRule,
    SocketAddr,
    "SocketAddress",
    "127.0.0.1:8080"
);

fn validate_host(value: &str) -> Result<(), TypeError> {
    if value.is_empty() {
        return Err(TypeError::empty("Host"));
    }
    if value.contains("://") || value.contains('/') || value.chars().any(char::is_whitespace) {
        return Err(TypeError::new(
            "Host",
            "host must not include a scheme, path, or whitespace",
            "provide a host like api.internal or 127.0.0.1",
        ));
    }
    Ok(())
}

fn parse_net<T>(value: &str, type_name: &'static str, example: &'static str) -> Result<T, TypeError>
where
    T: FromStr,
    T::Err: fmt::Display,
{
    value.parse::<T>().map_err(|error| {
        TypeError::new(
            type_name,
            format!("invalid network value: {error}"),
            format!("provide a value like {example}"),
        )
    })
}
