use lazy_static::lazy_static;
use prometheus::{HistogramVec, IntCounter, IntCounterVec, IntGaugeVec};
use tici_common::metrics::{
    OperationMetrics, OperationMetricsGuard, begin_operation_metrics,
    register_histogram_with_labels, register_int_counter, register_int_counter_with_labels,
    register_int_gauge_with_labels,
};

lazy_static! {
    pub static ref META_CLIENT_GRPC_HANDLE_DURATION_HISTOGRAM: HistogramVec =
        register_histogram_with_labels(
            "tici_meta_client_grpc_handle_duration",
            "The duration of shard meta client grpc calls",
            &["scope", "type"],
        );
    pub static ref META_CLIENT_GRPC_HANDLE_COUNTER: IntCounterVec =
        register_int_counter_with_labels(
            "tici_meta_client_grpc_handle_count",
            "The count of shard meta client grpc calls",
            &["scope", "type", "result"],
        );
    pub static ref META_CLIENT_GRPC_HANDLE_INFLIGHT_GAUGE: IntGaugeVec =
        register_int_gauge_with_labels(
            "tici_meta_client_grpc_handle_inflight",
            "The inflight count of shard meta client grpc calls",
            &["scope", "type"],
        );
    pub static ref META_CLIENT_POOL_FAILED_ATTEMPTS: IntCounter = register_int_counter(
        "tici_meta_client_pool_failed_attempts",
        "The count of failed attempts to get a meta client pool channel"
    );
}

pub fn begin_meta_client_metrics(rpc_type: &'static str) -> OperationMetricsGuard {
    begin_operation_metrics(
        OperationMetrics::new(
            &META_CLIENT_GRPC_HANDLE_DURATION_HISTOGRAM,
            &META_CLIENT_GRPC_HANDLE_COUNTER,
            &META_CLIENT_GRPC_HANDLE_INFLIGHT_GAUGE,
        ),
        "meta_client",
        rpc_type,
    )
}

pub fn inc_meta_client_pool_failed_attempts() {
    META_CLIENT_POOL_FAILED_ATTEMPTS.inc();
}

#[cfg(test)]
mod tests {
    use super::{
        META_CLIENT_GRPC_HANDLE_COUNTER, META_CLIENT_GRPC_HANDLE_INFLIGHT_GAUGE,
        META_CLIENT_POOL_FAILED_ATTEMPTS, begin_meta_client_metrics,
        inc_meta_client_pool_failed_attempts,
    };

    #[test]
    fn test_meta_client_metrics_guard_ok() {
        let inflight =
            META_CLIENT_GRPC_HANDLE_INFLIGHT_GAUGE.with_label_values(&["meta_client", "ut_ok"]);
        let counter =
            META_CLIENT_GRPC_HANDLE_COUNTER.with_label_values(&["meta_client", "ut_ok", "ok"]);
        let before_inflight = inflight.get();
        let before_counter = counter.get();

        {
            let _guard = begin_meta_client_metrics("ut_ok");
            assert_eq!(inflight.get(), before_inflight + 1);
        }

        assert_eq!(inflight.get(), before_inflight);
        assert_eq!(counter.get(), before_counter + 1);
    }

    #[test]
    fn test_meta_client_metrics_guard_error() {
        let counter =
            META_CLIENT_GRPC_HANDLE_COUNTER.with_label_values(&["meta_client", "ut_err", "error"]);
        let before_counter = counter.get();

        {
            let guard = begin_meta_client_metrics("ut_err");
            guard.mark_error();
        }

        assert_eq!(counter.get(), before_counter + 1);
    }

    #[test]
    fn test_meta_client_pool_failed_attempt_metric() {
        let before = META_CLIENT_POOL_FAILED_ATTEMPTS.get();

        inc_meta_client_pool_failed_attempts();

        assert_eq!(META_CLIENT_POOL_FAILED_ATTEMPTS.get(), before + 1);
    }
}
