/// axumserver 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 = "axumserver")]
pub mod axumserver {
    use crate::ip_addr;
    use crate::ip_addr::print_listening_with_protocol;
    use crate::logger::info;
    use axum::extract::Request;
    use axum::middleware::Next;
    use axum::response::Response;
    pub use axum::*;
    use hyper::HeaderMap;
    use hyper::StatusCode;
    pub async fn log_ip_middleware(
        headers: HeaderMap,
        request: Request<axum::body::Body>,
        next: Next,
    ) -> Result<Response, StatusCode> {
        let ip_port = request
            .extensions()
            .get::<axum::extract::ConnectInfo<std::net::SocketAddr>>()
            .map(|ci| (ci.0.ip(), ci.0.port()));
        if let Some(ip_port) = ip_port {
            info!("Ok get request ip_port: {}:{}", ip_port.0, ip_port.1);
            return Ok(next.run(request).await);
        } else {
            return Ok(next.run(request).await);
        }
    }

    use axum::response::IntoResponse;
    use std::borrow::Cow;
    use std::path::Path;
    use std::path::PathBuf;
    use tower::BoxError;
    pub async fn handle_error(error: BoxError) -> impl IntoResponse {
        if error.is::<tower::timeout::error::Elapsed>() {
            return (StatusCode::REQUEST_TIMEOUT, Cow::from("request timed out"));
        }

        if error.is::<tower::load_shed::error::Overloaded>() {
            return (
                StatusCode::SERVICE_UNAVAILABLE,
                Cow::from("service is overloaded, try again later"),
            );
        }

        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Cow::from(format!("Unhandled internal error: {error}")),
        )
    }

    use axum::routing::{get, post};
    use axum::{error_handling::HandleErrorLayer, extract::DefaultBodyLimit, Router};
    use hyper::Method;
    use std::sync::Arc;
    use std::time::Duration;
    use tower::ServiceBuilder;
    use tower_http::cors::Any;
    use tower_http::cors::CorsLayer;
    use tower_http::trace::TraceLayer;

    pub fn router() -> Router {
        Router::new()
            .layer(axum::middleware::from_fn(log_ip_middleware))
            .layer(DefaultBodyLimit::disable())
            .layer(
                ServiceBuilder::new()
                    .layer(HandleErrorLayer::new(handle_error))
                    .load_shed()
                    .concurrency_limit(2048)
                    .timeout(Duration::from_secs(10))
                    .layer(TraceLayer::new_for_http()),
            )
    }
    pub fn router_allow_cors() -> Router {
        let cors_layer = CorsLayer::new()
            .allow_origin(Any)
            .allow_headers(Any)
            .allow_private_network(true)
            .allow_methods(Any);
        Router::new()
            .layer(cors_layer)
            .layer(axum::middleware::from_fn(log_ip_middleware))
            .layer(DefaultBodyLimit::disable())
            .layer(
                ServiceBuilder::new()
                    .layer(HandleErrorLayer::new(handle_error))
                    .load_shed()
                    .concurrency_limit(2048)
                    .timeout(Duration::from_secs(10))
                    .layer(TraceLayer::new_for_http()),
            )
    }
    use crate::ip_addr::create_listener;
    use crate::ip_addr::get_addrs;
    use crate::ip_addr::print_listening;
    use crate::ip_addr::shutdown_signal;
    use crate::ip_addr::BindAddr;
    use crate::logger::error;
    use futures_util::future::join_all;
    use std::net::SocketAddr;
    use std::sync::atomic::{AtomicBool, Ordering};
    pub async fn server_app_default_ip(router: Router, port: u16) -> anyhow::Result<()> {
        let mut handles = vec![];
        let (mut ipv4_addrs, ipv6_addrs) = get_addrs()?;
        ipv4_addrs.extend(ipv6_addrs);
        let addrs = ipv4_addrs.clone();
        let listening = print_listening(&ipv4_addrs, port)?;
        info!("{}", listening);
        let running = Arc::new(AtomicBool::new(true));
        // Iterate through IPv4 addresses, create a listener for each and start async tasks
        for bind_addr in addrs.iter() {
            // Ensure the current address is an IP address type
            match bind_addr {
                BindAddr::IpAddr(ip) => {
                    // Create listener, bind IP address and server port
                    let listener = create_listener(SocketAddr::new(*ip, port))?;
                    let router = router.clone();
                    // Start async task to listen and handle client requests
                    let handle = tokio::spawn(async move {
                        // Create application instance with database connection pool and Minio client
                        let app = router;
                        // Start listening and handling requests
                        axum::serve(listener, app).await.unwrap();
                    });
                    // Add current task handle to the list
                    handles.push(handle);
                }
                #[cfg(unix)]
                BindAddr::SocketPath(ip) => {}
            }
        }
        // Start an async task that will terminate all tasks and gracefully shutdown the program when receiving shutdown signal
        handles.push(tokio::spawn(async move {
            // Wait for shutdown signal
            shutdown_signal().await;
            // Define exit code as success
            let code = std::process::ExitCode::SUCCESS;
            // Exit program
            std::process::exit(0);
        }));

        // Use tokio's select! macro to concurrently handle task completion and shutdown signals
        tokio::select! {
            // When all tasks complete
            ret = join_all(handles) => {
            // Iterate through each task's result
                for r in ret {
            // If task fails, log error message
                    if let Err(e) = r {
                        error!("Task failed: {}", e);
                    }
                }
            },
            // When shutdown signal is received
            _ = shutdown_signal() => {
            // Set running flag to false, indicating program will stop
                running.store(false, Ordering::SeqCst);
            },
        };

        Ok(())
    }

    #[cfg_attr(docsrs, doc(cfg(feature = "tls-rustls")))]
    pub async fn server_app_default_ip_with_tls(
        router: Router,
        port: u16,
        cert: PathBuf,
        key: PathBuf,
    ) -> anyhow::Result<()> {
        use rustls::crypto::ring::default_provider;
        use rustls::crypto::CryptoProvider;
        CryptoProvider::install_default(default_provider());
        
        let mut handles = vec![];
        let (mut ipv4_addrs, ipv6_addrs) = get_addrs()?;
        ipv4_addrs.extend(ipv6_addrs);
        let addrs = ipv4_addrs.clone();
        let listening = print_listening_with_protocol(&ipv4_addrs, port,"https")?;
        info!("{}", listening);
        let running = Arc::new(AtomicBool::new(true));
        // Iterate through IPv4 addresses, create a listener for each and start async tasks
        for bind_addr in addrs.iter() {
            // Ensure the current address is an IP address type
            match bind_addr {
                BindAddr::IpAddr(ip) => {
                    // Create listener, bind IP address and server port
                    // let listener = create_listener(SocketAddr::new(*ip, port))?;
                    let addr = SocketAddr::new(*ip, port);
                    let router = router.clone();
                    let cert = cert.clone();
                    let key = key.clone();
                    // Start async task to listen and handle client requests
                    let handle = tokio::spawn(async move {
                        // Create application instance with database connection pool and Minio client
                        let app = router;
                        // Start listening and handling requests
                        use axum_server::tls_rustls::RustlsConfig;

                        let config = RustlsConfig::from_pem_file(cert, key).await.unwrap();
                        axum_server::bind_rustls(addr, config)
                            .serve(app.into_make_service())
                            .await
                            .unwrap();
                        // axum::serve(listener, app).await.unwrap();
                    });
                    // Add current task handle to the list
                    handles.push(handle);
                }
                #[cfg(unix)]
                BindAddr::SocketPath(ip) => {}
            }
        }
        // Start an async task that will terminate all tasks and gracefully shutdown the program when receiving shutdown signal
        handles.push(tokio::spawn(async move {
            // Wait for shutdown signal
            shutdown_signal().await;
            // Define exit code as success
            let code = std::process::ExitCode::SUCCESS;
            // Exit program
            std::process::exit(0);
        }));

        // Use tokio's select! macro to concurrently handle task completion and shutdown signals
        tokio::select! {
            // When all tasks complete
            ret = join_all(handles) => {
            // Iterate through each task's result
                for r in ret {
            // If task fails, log error message
                    if let Err(e) = r {
                        error!("Task failed: {}", e);
                    }
                }
            },
            // When shutdown signal is received
            _ = shutdown_signal() => {
            // Set running flag to false, indicating program will stop
                running.store(false, Ordering::SeqCst);
            },
        };

        Ok(())
    }

    use std::net::IpAddr;
    pub async fn server_app(
        ip_addrs: Vec<IpAddr>,
        router: Router,
        port: u16,
    ) -> anyhow::Result<()> {
        let mut handles = vec![];
        let bind_ddr: Vec<BindAddr> = ip_addrs.iter().map(|ip| BindAddr::IpAddr(*ip)).collect();
        let listening = print_listening(&bind_ddr, port)?;
        info!("{}", listening);
        let running = Arc::new(AtomicBool::new(true));
        // Iterate through IPv4 addresses, create a listener for each and start async tasks
        for bind_addr in ip_addrs.iter() {
            // Create listener, bind IP address and server port
            let listener = create_listener(SocketAddr::new(*bind_addr, port))?;
            let router = router.clone();
            // Start async task to listen and handle client requests
            let handle = tokio::spawn(async move {
                // Create application instance with database connection pool and Minio client
                let app = router;
                // Start listening and handling requests
                axum::serve(listener, app).await.unwrap();
            });
            // Add current task handle to the list
            handles.push(handle);
        }
        // Start an async task that will terminate all tasks and gracefully shutdown the program when receiving shutdown signal
        handles.push(tokio::spawn(async move {
            // Wait for shutdown signal
            shutdown_signal().await;
            // Define exit code as success
            let code = std::process::ExitCode::SUCCESS;
            // Exit program
            std::process::exit(0);
        }));

        // Use tokio's select! macro to concurrently handle task completion and shutdown signals
        tokio::select! {
            // When all tasks complete
            ret = join_all(handles) => {
            // Iterate through each task's result
                for r in ret {
            // If task fails, log error message
                    if let Err(e) = r {
                        error!("Task failed: {}", e);
                    }
                }
            },
            // When shutdown signal is received
            _ = shutdown_signal() => {
            // Set running flag to false, indicating program will stop
                running.store(false, Ordering::SeqCst);
            },
        };

        Ok(())
    }
    #[cfg_attr(docsrs, doc(cfg(feature = "tls-rustls")))]
    pub async fn server_app_with_tls(
        ip_addrs: Vec<IpAddr>,
        router: Router,
        port: u16,
        cert: PathBuf,
        key: PathBuf,
    ) -> anyhow::Result<()> {
        use rustls::crypto::ring::default_provider;
        use rustls::crypto::CryptoProvider;
        CryptoProvider::install_default(default_provider());
        let mut handles = vec![];
        let bind_ddr: Vec<BindAddr> = ip_addrs.iter().map(|ip| BindAddr::IpAddr(*ip)).collect();
        let listening = print_listening_with_protocol(&bind_ddr, port,"https")?;
        info!("{}", listening);
        let running = Arc::new(AtomicBool::new(true));
        // Iterate through IPv4 addresses, create a listener for each and start async tasks
        for bind_addr in ip_addrs.iter() {
            // Create listener, bind IP address and server port
            let listener = create_listener(SocketAddr::new(*bind_addr, port))?;
            let router = router.clone();
            let addr = SocketAddr::new(*bind_addr, port);
            let cert = cert.clone();
            let key = key.clone();
            // Start async task to listen and handle client requests
            let handle = tokio::spawn(async move {
                // Create application instance with database connection pool and Minio client
                let app = router;
                use axum_server::tls_rustls::RustlsConfig;
                let config = RustlsConfig::from_pem_file(cert, key).await.unwrap();
                axum_server::bind_rustls(addr, config)
                    .serve(app.into_make_service())
                    .await
                    .unwrap();
            });
            // Add current task handle to the list
            handles.push(handle);
        }
        // Start an async task that will terminate all tasks and gracefully shutdown the program when receiving shutdown signal
        handles.push(tokio::spawn(async move {
            // Wait for shutdown signal
            shutdown_signal().await;
            // Define exit code as success
            let code = std::process::ExitCode::SUCCESS;
            // Exit program
            std::process::exit(0);
        }));

        // Use tokio's select! macro to concurrently handle task completion and shutdown signals
        tokio::select! {
            // When all tasks complete
            ret = join_all(handles) => {
            // Iterate through each task's result
                for r in ret {
            // If task fails, log error message
                    if let Err(e) = r {
                        error!("Task failed: {}", e);
                    }
                }
            },
            // When shutdown signal is received
            _ = shutdown_signal() => {
            // Set running flag to false, indicating program will stop
                running.store(false, Ordering::SeqCst);
            },
        };

        Ok(())
    }

}
#[cfg(feature = "axumserver")]
pub use axumserver::*;
