use actix_web::{web, HttpResponse};
use prometheus::proto::MetricFamily;
use prometheus::{
    Encoder, HistogramOpts, HistogramVec, IntCounter, IntCounterVec, Opts, Registry, TextEncoder,
};

use prometheus::core::{AtomicU64, GenericCounter};
use std::sync::Arc;

pub struct AppMetrics {
    pub scrape_requests_total: IntCounterVec,
    pub scrape_duration_seconds: HistogramVec,
    pub registry_checks_total: IntCounter,
    pub cache_hits_total: IntCounter,
    pub cache_misses_total: IntCounter,
}

impl AppMetrics {
    pub fn new(registry: &Registry) -> anyhow::Result<Self> {
        let scrape_requests_total = IntCounterVec::new(
            Opts::new("scrape_requests_total", "Total scrape requests"),
            &["config_name", "status"],
        )?;
        let scrape_duration_seconds = HistogramVec::new(
            HistogramOpts::new("scrape_duration_seconds", "Scrape duration in seconds"),
            &["config_name"],
        )?;
        let registry_checks_total: GenericCounter<AtomicU64> =
            IntCounter::new("registry_checks_total", "Total registry checks")?;
        let cache_hits_total: GenericCounter<AtomicU64> =
            IntCounter::new("cache_hits_total", "Total cache hits")?;
        let cache_misses_total: GenericCounter<AtomicU64> =
            IntCounter::new("cache_misses_total", "Total cache misses")?;

        registry.register(Box::new(scrape_requests_total.clone()))?;
        registry.register(Box::new(scrape_duration_seconds.clone()))?;
        registry.register(Box::new(registry_checks_total.clone()))?;
        registry.register(Box::new(cache_hits_total.clone()))?;
        registry.register(Box::new(cache_misses_total.clone()))?;

        Ok(Self {
            scrape_requests_total,
            scrape_duration_seconds,
            registry_checks_total,
            cache_hits_total,
            cache_misses_total,
        })
    }
}

pub async fn metrics_handler(registry: web::Data<Arc<Registry>>) -> HttpResponse {
    let encoder: TextEncoder = TextEncoder::new();
    let metric_families: Vec<MetricFamily> = registry.gather();
    let mut buffer: Vec<u8> = Vec::new();
    if let Err(e) = encoder.encode(&metric_families, &mut buffer) {
        tracing::error!("Failed to encode metrics: {}", e);
        return HttpResponse::InternalServerError().finish();
    }
    HttpResponse::Ok()
        .content_type("text/plain; version=0.0.4")
        .body(buffer)
}
