//! Metrics collection module with basic Prometheus integration
//!
//! This module provides basic metrics collection for monitoring:
//! - HTTP request metrics (count, duration, status codes)
//! - Database connection metrics
//! - System resource metrics

#![allow(dead_code)]

use axum::{Router, extract::State, http::StatusCode, response::Response, routing::get};
use prometheus::{Encoder, IntCounter, IntGauge, Registry, TextEncoder};
use std::sync::Arc;
use std::time::Instant;
use tracing::{debug, error, warn};

use crate::AppState;

/// Basic metrics registry
pub struct MetricsRegistry {
    registry: Arc<Registry>,
    http_requests_total: IntCounter,
    http_requests_in_flight: IntGauge,
    db_connections_active: IntGauge,
    auth_requests_total: IntCounter,
    content_requests_total: IntCounter,
    email_sent_total: IntCounter,
}

impl MetricsRegistry {
    /// Create a new metrics registry with basic metrics
    pub fn new() -> Result<Self, prometheus::Error> {
        let registry = Arc::new(Registry::new());

        // HTTP metrics
        let http_requests_total =
            IntCounter::new("http_requests_total", "Total number of HTTP requests")?;

        let http_requests_in_flight = IntGauge::new(
            "http_requests_in_flight",
            "Current number of HTTP requests being processed",
        )?;

        // Database metrics
        let db_connections_active = IntGauge::new(
            "db_connections_active",
            "Number of active database connections",
        )?;

        // Authentication metrics
        let auth_requests_total = IntCounter::new(
            "auth_requests_total",
            "Total number of authentication requests",
        )?;

        // Content service metrics
        let content_requests_total =
            IntCounter::new("content_requests_total", "Total number of content requests")?;

        // Email service metrics
        let email_sent_total = IntCounter::new("email_sent_total", "Total number of emails sent")?;

        // Register all metrics
        registry.register(Box::new(http_requests_total.clone()))?;
        registry.register(Box::new(http_requests_in_flight.clone()))?;
        registry.register(Box::new(db_connections_active.clone()))?;
        registry.register(Box::new(auth_requests_total.clone()))?;
        registry.register(Box::new(content_requests_total.clone()))?;
        registry.register(Box::new(email_sent_total.clone()))?;

        Ok(Self {
            registry,
            http_requests_total,
            http_requests_in_flight,
            db_connections_active,
            auth_requests_total,
            content_requests_total,
            email_sent_total,
        })
    }

    /// Get the underlying registry
    pub fn registry(&self) -> &Registry {
        &self.registry
    }

    /// Increment HTTP request counter
    pub fn inc_http_requests(&self) {
        self.http_requests_total.inc();
    }

    /// Increment HTTP requests in flight
    pub fn inc_http_in_flight(&self) {
        self.http_requests_in_flight.inc();
    }

    /// Decrement HTTP requests in flight
    pub fn dec_http_in_flight(&self) {
        self.http_requests_in_flight.dec();
    }

    /// Set database connections active
    pub fn set_db_connections_active(&self, count: i64) {
        self.db_connections_active.set(count);
    }

    /// Increment auth requests
    pub fn inc_auth_requests(&self) {
        self.auth_requests_total.inc();
    }

    /// Increment content requests
    pub fn inc_content_requests(&self) {
        self.content_requests_total.inc();
    }

    /// Increment emails sent
    pub fn inc_emails_sent(&self) {
        self.email_sent_total.inc();
    }
}

/// Metrics service for collecting application metrics
pub struct MetricsService {
    registry: Option<Arc<MetricsRegistry>>,
}

impl MetricsService {
    /// Create a new metrics service
    pub fn new() -> Result<Self, prometheus::Error> {
        let registry = MetricsRegistry::new()?;
        Ok(Self {
            registry: Some(Arc::new(registry)),
        })
    }

    /// Get metrics registry
    pub fn registry(&self) -> Option<&Arc<MetricsRegistry>> {
        self.registry.as_ref()
    }

    /// Record HTTP request
    #[allow(dead_code)]
    pub fn record_http_request(&self) {
        if let Some(registry) = &self.registry {
            registry.inc_http_requests();
        }
    }

    /// Record HTTP request start
    #[allow(dead_code)]
    pub fn record_http_request_start(&self) {
        if let Some(registry) = &self.registry {
            registry.inc_http_in_flight();
        }
    }

    /// Record HTTP request end
    #[allow(dead_code)]
    pub fn record_http_request_end(&self) {
        if let Some(registry) = &self.registry {
            registry.dec_http_in_flight();
        }
    }

    /// Record database connection count
    #[allow(dead_code)]
    pub fn record_db_connections(&self, count: i64) {
        if let Some(registry) = &self.registry {
            registry.set_db_connections_active(count);
        }
    }

    /// Record authentication request
    #[allow(dead_code)]
    pub fn record_auth_request(&self) {
        if let Some(registry) = &self.registry {
            registry.inc_auth_requests();
        }
    }

    /// Record content request
    #[allow(dead_code)]
    pub fn record_content_request(&self) {
        if let Some(registry) = &self.registry {
            registry.inc_content_requests();
        }
    }

    /// Record email sent
    #[allow(dead_code)]
    pub fn record_email_sent(&self) {
        if let Some(registry) = &self.registry {
            registry.inc_emails_sent();
        }
    }
}

/// HTTP middleware for collecting request metrics
pub async fn metrics_middleware(
    State(state): State<AppState>,
    request: axum::extract::Request,
    next: axum::middleware::Next,
) -> Response {
    let start = Instant::now();

    // Record request start
    if let Some(metrics) = state.metrics_registry.as_ref() {
        metrics.inc_http_requests();
        metrics.inc_http_in_flight();
    }

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

    // Record request end
    if let Some(metrics) = state.metrics_registry.as_ref() {
        metrics.dec_http_in_flight();
    }

    let duration = start.elapsed();
    debug!("Request completed in {:?}", duration);

    response
}

/// Handlers for metrics endpoints
pub mod handlers {
    use super::*;
    use axum::body::Body;
    use axum::http::header;
    use axum::response::Response;

    /// Prometheus metrics endpoint
    pub async fn metrics(State(state): State<AppState>) -> Result<Response<Body>, StatusCode> {
        if let Some(metrics) = state.metrics_registry.as_ref() {
            let encoder = TextEncoder::new();
            let metric_families = metrics.registry().gather();

            match encoder.encode_to_string(&metric_families) {
                Ok(output) => {
                    debug!("Serving metrics endpoint");
                    Ok(Response::builder()
                        .header(header::CONTENT_TYPE, encoder.format_type())
                        .body(Body::from(output))
                        .unwrap())
                }
                Err(e) => {
                    error!("Failed to encode metrics: {}", e);
                    Err(StatusCode::INTERNAL_SERVER_ERROR)
                }
            }
        } else {
            warn!("Metrics registry not available");
            Err(StatusCode::SERVICE_UNAVAILABLE)
        }
    }

    /// Health metrics endpoint (JSON format)
    pub async fn health_metrics(
        State(state): State<AppState>,
    ) -> Result<axum::Json<serde_json::Value>, StatusCode> {
        if let Some(metrics) = state.metrics_registry.as_ref() {
            let metric_families = metrics.registry().gather();

            let mut json_metrics = serde_json::Map::new();
            json_metrics.insert(
                "status".to_string(),
                serde_json::Value::String("healthy".to_string()),
            );
            json_metrics.insert(
                "metrics_count".to_string(),
                serde_json::Value::Number(metric_families.len().into()),
            );

            Ok(axum::Json(serde_json::Value::Object(json_metrics)))
        } else {
            warn!("Metrics registry not available");
            Err(StatusCode::SERVICE_UNAVAILABLE)
        }
    }

    /// Readiness probe endpoint
    pub async fn readiness_probe(
        State(state): State<AppState>,
    ) -> Result<axum::Json<serde_json::Value>, StatusCode> {
        // Basic readiness check
        let ready = state.metrics_registry.is_some();

        let mut response = serde_json::Map::new();
        response.insert("ready".to_string(), serde_json::Value::Bool(ready));

        if ready {
            Ok(axum::Json(serde_json::Value::Object(response)))
        } else {
            Err(StatusCode::SERVICE_UNAVAILABLE)
        }
    }

    /// Liveness probe endpoint
    pub async fn liveness_probe() -> axum::Json<serde_json::Value> {
        let mut response = serde_json::Map::new();
        response.insert("alive".to_string(), serde_json::Value::Bool(true));
        axum::Json(serde_json::Value::Object(response))
    }
}

/// Create metrics routes
pub fn create_metrics_routes() -> Router<AppState> {
    Router::new()
        .route("/metrics", get(handlers::metrics))
        .route("/metrics/health", get(handlers::health_metrics))
        .route("/health/ready", get(handlers::readiness_probe))
        .route("/health/live", get(handlers::liveness_probe))
}

/// Extension trait for AppState to include metrics
impl AppState {
    pub fn metrics_registry(&self) -> Option<&Arc<MetricsRegistry>> {
        self.metrics_registry.as_ref()
    }
}

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

    #[test]
    fn test_metrics_registry_creation() {
        let registry = MetricsRegistry::new();
        assert!(registry.is_ok());
    }

    #[test]
    fn test_metrics_service_creation() {
        let service = MetricsService::new();
        assert!(service.is_ok());
    }

    #[test]
    fn test_metrics_operations() {
        let service = MetricsService::new().unwrap();

        // Test recording various metrics
        service.record_http_request();
        service.record_auth_request();
        service.record_content_request();
        service.record_email_sent();
        service.record_db_connections(5);

        // Basic smoke test - if we get here without panicking, the operations work
        assert!(service.registry().is_some());
    }
}
