use crate::{handler::Handler, ctx::Ctx};

pub struct LoggingMiddleware {
    _p: (),
}

impl LoggingMiddleware {
    pub fn new() -> Self {
        Self { _p: () }
    }
}

struct FormattedDuration(std::time::Duration);

impl std::fmt::Display for FormattedDuration {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let nanos = self.0.as_nanos() as u64;
        if nanos >= 1_000_000_000 {
            let ms = nanos / 1_000_000;
            write!(f, "{}.{:03}s", ms / 1000, ms % 1000)
        } else if nanos >= 1_000_000 {
            let us = nanos / 1_000;
            write!(f, "{}.{:03}ms", us / 1000, us % 1000)
        } else {
            write!(f, "{}.{:03}µs", nanos / 1000, nanos % 1000)
        }
    }
}

impl Handler<&mut Ctx> for LoggingMiddleware {
    type Output = ();

    async fn call(&self, c: &mut Ctx) -> Self::Output {
        let time = std::time::Instant::now();

        c.next().await;

        tracing::info!(
            "{} | {:^10} | {} | {:^7} | {}",
            c.res.inner.status().as_u16(),
            FormattedDuration(time.elapsed()),
            c.req.ip(),
            c.req.method().as_str(),
            c.req.uri().path(),
        );
    }
}
