/// ip_addr mod
/// ```ignore
/// #[allow(warnings)]
/// fn main() {
///     let rt = doe::asyncrs::runtime::Builder::new_multi_thread()
///         .enable_all()
///         .build()
///         .unwrap();
///     rt.block_on(run());
///     rt.block_on(async {
///         run().await;
///     });
/// }
/// 
/// async fn run() {
///     use doe::logger::*;
///     doe::logger::init_info();
///     debug!("开始服务");
///     use doe::axumserver::response::Html;
///     use doe::axumserver::routing::get;
///     pub async fn home() -> Html<String> {
///         include_str!("../index.html").to_string().into()
///     }
///     let router = doe::axumserver::router_allow_cors().route("/", get(home));
///     doe::axumserver::server_app_default_ip(router, 60001)
///         .await
///         .unwrap();
///     // doe::axumserver::server_app( vec![IpAddr::from([127, 0, 0, 1])],router, 60001).await;
/// }
/// ```
///
#[allow(warnings)]
#[cfg(feature = "ip_addr")]
pub mod ip_addr {
    use anyhow::{Context, Result};
    use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
    use tokio::net::TcpListener;
    
    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
    pub enum BindAddr {
        IpAddr(IpAddr),
        #[cfg(unix)]
        SocketPath(String),
    }
    impl BindAddr {
        fn parse_addrs(addrs: &[&str]) -> Result<Vec<Self>> {
            let mut bind_addrs = vec![];
            #[cfg(not(unix))]
            let mut invalid_addrs = vec![];
            for addr in addrs {
                match addr.parse::<IpAddr>() {
                    Ok(v) => {
                        bind_addrs.push(BindAddr::IpAddr(v));
                    }
                    Err(_) => {
                        #[cfg(unix)]
                        bind_addrs.push(BindAddr::SocketPath(addr.to_string()));
                        #[cfg(not(unix))]
                        invalid_addrs.push(*addr);
                    }
                }
            }
            #[cfg(not(unix))]
            if !invalid_addrs.is_empty() {
                anyhow::bail!("Invalid bind address `{}`", invalid_addrs.join(","));
            }
            Ok(bind_addrs)
        }
    }
    pub fn interface_addrs() -> Result<(Vec<BindAddr>, Vec<BindAddr>)> {
        let (mut ipv4_addrs, mut ipv6_addrs) = (vec![], vec![]);
        let ifaces =
            if_addrs::get_if_addrs().with_context(|| "Failed to get local interface addresses")?;
        for iface in ifaces.into_iter() {
            let ip = iface.ip();
            if ip.is_ipv4() {
                ipv4_addrs.push(BindAddr::IpAddr(ip))
            }
            if ip.is_ipv6() {
                ipv6_addrs.push(BindAddr::IpAddr(ip))
            }
        }
        Ok((ipv4_addrs, ipv6_addrs))
    }
    
    pub async fn shutdown_signal() {
        tokio::signal::ctrl_c()
            .await
            .expect("Failed to install CTRL+C signal handler")
    }
    pub fn print_listening(print_addrs: &[BindAddr], port: u16) -> Result<String> {
        let mut output = String::new();
        let urls = print_addrs
            .iter()
            .map(|bind_addr| match bind_addr {
                BindAddr::IpAddr(addr) => {
                    let addr = match addr {
                        IpAddr::V4(_) => format!("{}:{}", addr, port),
                        IpAddr::V6(_) => format!("[{}]:{}", addr, port),
                    };
                    let protocol = "http";
                    format!("{}://{}", protocol, addr)
                }
                #[cfg(unix)]
                BindAddr::SocketPath(path) => path.to_string(),
            })
            .collect::<Vec<_>>();
    
        if urls.len() == 1 {
            output.push_str(&format!("Listening on {}", urls[0]))
        } else {
            let info = urls
                .iter()
                .map(|v| format!("  {v}"))
                .collect::<Vec<String>>()
                .join("\n");
            output.push_str(&format!("Listening on:\n{info}\n"))
        }
    
        Ok(output)
    }
    
    pub fn print_listening_with_protocol(print_addrs: &[BindAddr], port: u16,protocol:&str) -> Result<String> {
        let mut output = String::new();
        let urls = print_addrs
            .iter()
            .map(|bind_addr| match bind_addr {
                BindAddr::IpAddr(addr) => {
                    let addr = match addr {
                        IpAddr::V4(_) => format!("{}:{}", addr, port),
                        IpAddr::V6(_) => format!("[{}]:{}", addr, port),
                    };
                    format!("{}://{}", protocol, addr)
                }
                #[cfg(unix)]
                BindAddr::SocketPath(path) => path.to_string(),
            })
            .collect::<Vec<_>>();
    
        if urls.len() == 1 {
            output.push_str(&format!("Listening on {}", urls[0]))
        } else {
            let info = urls
                .iter()
                .map(|v| format!("  {v}"))
                .collect::<Vec<String>>()
                .join("\n");
            output.push_str(&format!("Listening on:\n{info}\n"))
        }
    
        Ok(output)
    }
    
    pub fn create_listener(addr: SocketAddr) -> Result<TcpListener> {
        use socket2::{Domain, Protocol, Socket, Type};
        let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?;
        if addr.is_ipv6() {
            socket.set_only_v6(true)?;
        }
        socket.set_reuse_address(true)?;
        socket.bind(&addr.into())?;
        socket.listen(1024 /* Default backlog */)?;
        let std_listener = StdTcpListener::from(socket);
        std_listener.set_nonblocking(true)?;
        let listener = TcpListener::from_std(std_listener)?;
        Ok(listener)
    }
    
    pub fn get_addrs() -> Result<(Vec<BindAddr>, Vec<BindAddr>)> {
        let (ipv4_addrs, ipv6_addrs) = interface_addrs()?;
        Ok((ipv4_addrs, ipv6_addrs))
    }
    
}
#[cfg(feature = "ip_addr")]
pub use ip_addr::*;
