use http_body_util::BodyExt;
use hyper::body::Incoming;
use hyper::server::conn::http1;
use hyper::{service::service_fn, Request};
use hyper_util::rt::TokioIo;
use ngyn_shared::core::{NgynPlatform, PlatformData};
use std::sync::Arc;
use tokio::net::TcpListener;
#[derive(Default)]
pub struct HyperApplication {
    data: PlatformData,
}
impl NgynPlatform for HyperApplication {
    fn data_mut(&mut self) -> &mut PlatformData {
        &mut self.data
    }
}
impl HyperApplication {
    pub async fn listen<A: tokio::net::ToSocketAddrs>(
        self,
        address: A,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let server = TcpListener::bind(address).await?;
        let data = Arc::new(self.data);
        let service = service_fn(move |req: Request<Incoming>| {
            let data = data.clone();
            async move {
                let (parts, mut body) = req.into_parts();
                let body = {
                    let mut buf = Vec::new();
                    if let Some(frame) = body.frame().await {
                        let chunk = frame?.into_data();
                        if let Ok(bytes) = chunk {
                            buf.extend_from_slice(&bytes)
                        }
                    }
                    buf
                };
                let req = Request::from_parts(parts, body);
                let res = data.respond(req).await;
                Ok::<_, hyper::Error>(res)
            }
        });
        let http = http1::Builder::new();
        loop {
            tokio::select! {
                Ok((stream, _)) = server.accept() => {
                    let io = TokioIo::new(stream);
                    let conn = http.serve_connection(io, service.clone());
                    tokio::task::spawn(async move {
                        if let Err(e) = conn.await {
                            eprintln!("server error: {}", e);
                        }
                    });
                }
            }
        }
    }
}