use std::time::Duration;

use lazy_static::lazy_static;
use prometheus::{
    Histogram, HistogramTimer, HistogramVec, IntCounter, IntCounterVec, exponential_buckets,
};
use tici_common::metrics::{
    register_histogram_with_buckets, register_histogram_with_labels, register_int_counter,
    register_int_counter_with_labels,
};

lazy_static! {
    static ref WRITER_CDC_EVENT_COUNTER: IntCounterVec = register_int_counter_with_labels(
        "tici_writer_cdc_event_count",
        "Number of cdc events",
        &["type"],
    );
    static ref WRITER_CDC_OPERATION_COUNTER: IntCounterVec = register_int_counter_with_labels(
        "tici_writer_cdc_operation_count",
        "Number of cdc operations",
        &["type"],
    );
    static ref WRITER_CDC_OPERATION_LATENCY: HistogramVec = register_histogram_with_labels(
        "tici_writer_cdc_operation_latency",
        "Latency of cdc operations (s)",
        &["type"],
    );
    pub static ref WRITER_FRAG_OPERATION_COUNTER: IntCounterVec = register_int_counter_with_labels(
        "tici_writer_frag_operation_count",
        "Number of fragment operations",
        &["type"],
    );
    pub static ref WRITER_FRAG_OPERATION_LATENCY: HistogramVec = register_histogram_with_labels(
        "tici_writer_frag_operation_latency",
        "Latency of fragment operations (ms)",
        &["type"],
    );
    static ref WRITER_FRAG_UPLOAD_GROUP_COUNT: Histogram = register_histogram_with_buckets(
        "tici_writer_frag_upload_group_count",
        "The number of upload groups produced by one fragment upload request",
        exponential_buckets(1.0, 2.0, 11).unwrap(),
    );
    static ref WRITER_FRAG_UPLOAD_GROUP_FILE_COUNT: Histogram = register_histogram_with_buckets(
        "tici_writer_frag_upload_group_file_count",
        "The number of files contained in one fragment upload group",
        exponential_buckets(1.0, 2.0, 11).unwrap(),
    );
    static ref WRITER_FRAG_UPLOAD_GROUP_BYTES: Histogram = register_histogram_with_buckets(
        "tici_writer_frag_upload_group_bytes",
        "The number of bytes contained in one fragment upload group",
        exponential_buckets(1024.0, 2.0, 23).unwrap(),
    );
    // For a group of N files, add N - 1 to represent how many uploads are saved compared with
    // uploading every file separately. Summed across a request, this equals total files minus
    // total groups.
    static ref WRITER_FRAG_UPLOAD_MERGED_FILE_COUNTER: IntCounter =
        register_int_counter(
            "tici_writer_frag_upload_merged_file_count",
            "The number of files merged into existing fragment upload groups",
        );
}

pub fn inc_cdc_op_count(op_type: &str) {
    inc_cdc_op_count_by(op_type, 1);
}

pub fn inc_cdc_op_count_by(op_type: &str, count: u64) {
    WRITER_CDC_OPERATION_COUNTER
        .with_label_values(&[op_type])
        .inc_by(count);
}

pub fn inc_cdc_event_count(event_type: &str) {
    WRITER_CDC_EVENT_COUNTER
        .with_label_values(&[event_type])
        .inc();
}

#[cfg(test)]
pub fn get_cdc_event_count(event_type: &str) -> u64 {
    WRITER_CDC_EVENT_COUNTER
        .with_label_values(&[event_type])
        .get()
}

pub fn start_cdc_op_timer(op_type: &str) -> HistogramTimer {
    WRITER_CDC_OPERATION_LATENCY
        .with_label_values(&[op_type])
        .start_timer()
}

pub fn observe_cdc_op_latency(op_type: &str, latency: Duration) {
    WRITER_CDC_OPERATION_LATENCY
        .with_label_values(&[op_type])
        .observe(latency.as_secs_f64());
}

pub fn observe_frag_upload_group_count(group_count: usize) {
    WRITER_FRAG_UPLOAD_GROUP_COUNT.observe(group_count as f64);
}

pub fn observe_frag_upload_group_file_count(file_count: usize) {
    WRITER_FRAG_UPLOAD_GROUP_FILE_COUNT.observe(file_count as f64);
}

pub fn observe_frag_upload_group_bytes(bytes: u64) {
    WRITER_FRAG_UPLOAD_GROUP_BYTES.observe(bytes as f64);
}

pub fn inc_frag_upload_merged_file_count(count: usize) {
    if count == 0 {
        return;
    }
    WRITER_FRAG_UPLOAD_MERGED_FILE_COUNTER.inc_by(count as u64);
}
