//! Prometheus Metrics Exporter
//!
//! Provides HTTP endpoint for Prometheus scraping at /metrics

use prometheus::{Encoder, Registry, TextEncoder};
use std::sync::Arc;
use tonic::{Response, Status};

/// Prometheus exporter for metrics collection
#[derive(Clone)]
pub struct PrometheusExporter {
    registry: Arc<Registry>,
}

impl PrometheusExporter {
    /// Create new exporter with registry
    pub fn new(registry: Arc<Registry>) -> Self {
        Self { registry }
    }

    /// Gather and encode metrics in Prometheus text format
    pub fn gather_metrics(&self) -> Result<String, prometheus::Error> {
        let encoder = TextEncoder::new();
        let metric_families = self.registry.gather();
        let mut buffer = vec![];
        encoder.encode(&metric_families, &mut buffer)?;
        String::from_utf8(buffer).map_err(|e| {
            prometheus::Error::Msg(format!("Failed to convert metrics to UTF-8: {}", e))
        })
    }

    /// Export metrics as `HTTP` response (for Axum/Hyper integration)
    pub fn export_http(&self) -> Result<Vec<u8>, prometheus::Error> {
        let encoder = TextEncoder::new();
        let metric_families = self.registry.gather();
        let mut buffer = vec![];
        encoder.encode(&metric_families, &mut buffer)?;
        Ok(buffer)
    }
}

/// Create Prometheus metrics endpoint (for standalone `HTTP` server)
///
/// Usage with Axum:
/// ```rust,ignore
/// use axum::{Router, routing::get};
/// use api::metrics::{GatewayMetrics, metrics_router};
///
/// let metrics = GatewayMetrics::new()?;
/// let router = metrics_router(metrics.registry());
/// ```
pub fn metrics_router(registry: Arc<Registry>) -> axum::Router {
    use axum::{routing::get, Router};

    let exporter = PrometheusExporter::new(registry);

    Router::new().route(
        "/metrics",
        get(move || {
            let exporter = exporter.clone();
            async move {
                match exporter.gather_metrics() {
                    Ok(metrics) => Ok(metrics),
                    Err(e) => Err(format!("Failed to gather metrics: {}", e)),
                }
            }
        }),
    )
}

/// Create combined metrics and health router
///
/// This combines Prometheus metrics with health/resilience endpoints
pub fn combined_router(registry: Arc<Registry>) -> axum::Router {
    use crate::health_router::{health_router, HealthState};
    use axum::Router;

    let metrics_routes = metrics_router(registry);
    let health_state = HealthState::new();
    let health_routes = health_router(health_state);

    Router::new().merge(metrics_routes).merge(health_routes)
}

/// Create Prometheus endpoint as gRPC health check extension
///
/// This allows exposing metrics through the same gRPC server
pub async fn serve_metrics_grpc(registry: Arc<Registry>) -> Result<Response<String>, Status> {
    let exporter = PrometheusExporter::new(registry);

    exporter
        .gather_metrics()
        .map(Response::new)
        .map_err(|e| Status::internal(format!("Failed to gather metrics: {}", e)))
}

#[cfg(test)]
mod tests {
    use super::*;
    use prometheus::{Counter, Opts};

    #[test]
    fn test_prometheus_exporter() {
        let registry = Arc::new(Registry::new());

        // Register test counter
        let counter = Counter::with_opts(Opts::new("test_counter", "Test counter")).unwrap();
        registry.register(Box::new(counter.clone())).unwrap();

        counter.inc();
        counter.inc();

        let exporter = PrometheusExporter::new(registry);
        let metrics = exporter.gather_metrics().unwrap();

        assert!(metrics.contains("test_counter"));
        assert!(metrics.contains("# HELP test_counter Test counter"));
        assert!(metrics.contains("test_counter 2"));
    }

    #[test]
    fn test_http_export() {
        let registry = Arc::new(Registry::new());
        let counter = Counter::with_opts(Opts::new("http_test", "HTTP test")).unwrap();
        registry.register(Box::new(counter.clone())).unwrap();

        counter.inc_by(42.0);

        let exporter = PrometheusExporter::new(registry);
        let buffer = exporter.export_http().unwrap();
        let metrics = String::from_utf8(buffer).expect("INVARIANT: Valid UTF-8 bytes");

        assert!(metrics.contains("http_test"));
        assert!(metrics.contains("42"));
    }
}
