use std::sync::OnceLock;
use std::time::Instant;

use axum::{extract::Request, middleware::Next, response::Response};
use metrics::{counter, histogram};
use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};

static METRICS_HANDLE: OnceLock<PrometheusHandle> = OnceLock::new();

pub fn setup_metrics_recorder() {
    let handle = PrometheusBuilder::new()
        .install_recorder()
        .expect("failed to install metrics recorder");
    METRICS_HANDLE
        .set(handle)
        .expect("metrics recorder already initialized");
}

pub async fn track_metrics(req: Request, next: Next) -> Response {
    let method = req.method().to_string();
    let path = req.uri().path().to_string();
    let start = Instant::now();

    let response = next.run(req).await;

    let status = response.status().as_u16().to_string();
    let duration = start.elapsed().as_secs_f64();

    let labels = [
        ("method", method),
        ("path", path),
        ("status", status),
    ];

    counter!("http_requests_total", &labels).increment(1);
    histogram!("http_request_duration_seconds", &labels).record(duration);

    response
}

pub async fn metrics_handler() -> String {
    METRICS_HANDLE
        .get()
        .map(|h| h.render())
        .unwrap_or_default()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn recorder_renders_metrics() {
        let handle = setup_metrics_recorder();
        metrics::counter!("test_counter").increment(1);
        metrics::gauge!("test_gauge").set(42.0);
        let output = handle.render();
        assert!(output.contains("test_counter"), "missing counter in: {output}");
        assert!(output.contains("test_gauge"), "missing gauge in: {output}");
    }
}
