Documentation
use http::{Extensions, Request, StatusCode};
use hyper::{body::Incoming, service::service_fn};
use hyper_util::{
    rt::{TokioExecutor, TokioIo},
    server::conn::auto::Builder,
};
use mincat_core::{
    body::Body,
    response::{IntoResponse, Response},
    router::Router,
};
use std::{convert::Infallible, net::SocketAddr, sync::Arc, time::Duration};
use tokio::net::TcpListener;

#[derive(Debug, Clone)]
pub struct MincatRoutePath(pub String);

#[derive(Clone, Default)]
pub struct App {
    router: Arc<Router>,
    state: Extensions,
}

impl App {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn router(&mut self, router: Router) -> Self {
        let mut self_router = self.router.clone();
        let self_router = Arc::make_mut(&mut self_router);
        self.router = Arc::new(self_router.merge(router));
        self.clone()
    }

    pub fn state<T>(&mut self, state: T) -> Self
    where
        T: Clone + Send + Sync + 'static,
    {
        self.state.insert(state);
        self.clone()
    }

    pub async fn run(&mut self, addr: &str) {
        let addr = addr.parse::<SocketAddr>().expect("addr parse failed");
        let listener = TcpListener::bind(addr).await.expect("tcp bind failed");

        loop {
            let (stream, _) = match listener.accept().await {
                Ok(conn) => conn,
                Err(e) => {
                    tracing::error!("accept error: {e}");
                    tokio::time::sleep(Duration::from_secs(1)).await;
                    continue;
                }
            };
            let io = TokioIo::new(stream);
            let router = self.router.clone();
            let state = self.state.clone();
            let service = service_fn(move |mut request| {
                let router = router.clone();
                let state = state.clone();
                request.extensions_mut().extend(state);
                handler(router, request)
            });

            tokio::task::spawn(async move {
                if let Err(e) = Builder::new(TokioExecutor::new())
                    .serve_connection_with_upgrades(io, service)
                    .await
                {
                    tracing::error!("serve_connection_with_upgrades error: {e}");
                }
            });
        }
    }
}

async fn handler(router: Arc<Router>, request: Request<Incoming>) -> Result<Response, Infallible> {
    let mut request = request.map(Body::new);
    let path = request.uri().path();
    let method = request.method();

    if let Some((define_path, handler)) = router.get_handler(method, path) {
        request
            .extensions_mut()
            .insert(MincatRoutePath(define_path));

        return Ok(handler.exectue(request).await);
    }

    Ok(StatusCode::NOT_FOUND.into_response())
}