use std::collections::HashMap;
use substrate_prometheus_endpoint::{register, Counter, Gauge, PrometheusError, Registry, U64};
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Event {
    Broadcast,
    SendRequest,
    SendTo,
    SendExtensionRequest,
    HandleState,
    HandleRequestResponse,
    HandleRequest,
    HandleExtensionRequest,
    HandleTask,
    HandleBlockImported,
    HandleBlockFinalized,
    HandleStateResponse,
    HandleJustificationFromUser,
    HandleInternalRequest,
}
use Event::*;
use crate::BlockNumber;
impl Event {
    fn name(&self) -> &str {
        match self {
            Broadcast => "broadcast",
            SendRequest => "send_request",
            SendTo => "send_to",
            SendExtensionRequest => "send_extension_request",
            HandleState => "handle_state",
            HandleRequestResponse => "handle_request_response",
            HandleRequest => "handle_request",
            HandleExtensionRequest => "handle_extension_request",
            HandleTask => "handle_task",
            HandleBlockImported => "handle_block_imported",
            HandleBlockFinalized => "handle_block_finalized",
            HandleStateResponse => "handle_state_response",
            HandleJustificationFromUser => "handle_justification_from_user",
            HandleInternalRequest => "handle_internal_request",
        }
    }
}
const ALL_EVENTS: [Event; 14] = [
    Broadcast,
    SendRequest,
    SendTo,
    SendExtensionRequest,
    HandleState,
    HandleRequestResponse,
    HandleRequest,
    HandleExtensionRequest,
    HandleTask,
    HandleBlockImported,
    HandleBlockFinalized,
    HandleStateResponse,
    HandleJustificationFromUser,
    HandleInternalRequest,
];
const ERRORING_EVENTS: [Event; 11] = [
    Broadcast,
    SendRequest,
    SendTo,
    SendExtensionRequest,
    HandleState,
    HandleRequest,
    HandleExtensionRequest,
    HandleTask,
    HandleBlockImported,
    HandleJustificationFromUser,
    HandleInternalRequest,
];
pub enum Metrics {
    Prometheus {
        event_calls: HashMap<Event, Counter<U64>>,
        event_errors: HashMap<Event, Counter<U64>>,
        top_finalized_block: Gauge<U64>,
        best_block: Gauge<U64>,
    },
    Noop,
}
impl Metrics {
    pub fn new(registry: Option<Registry>) -> Result<Self, PrometheusError> {
        let registry = match registry {
            Some(registry) => registry,
            None => return Ok(Metrics::Noop),
        };
        let mut event_calls = HashMap::new();
        let mut event_errors = HashMap::new();
        for event in ALL_EVENTS {
            event_calls.insert(
                event,
                register(
                    Counter::new(
                        format!("phron_sync_{}", event.name()),
                        format!("number of times {} has been called", event.name()),
                    )?,
                    ®istry,
                )?,
            );
        }
        for event in ERRORING_EVENTS {
            event_errors.insert(
                event,
                register(
                    Counter::new(
                        format!("phron_sync_{}_error", event.name()),
                        format!("number of times {} has returned an error", event.name()),
                    )?,
                    ®istry,
                )?,
            );
        }
        Ok(Metrics::Prometheus {
            event_calls,
            event_errors,
            top_finalized_block: register(
                Gauge::new("phron_top_finalized_block", "no help")?,
                ®istry,
            )?,
            best_block: register(Gauge::new("phron_best_block", "no help")?, ®istry)?,
        })
    }
    pub fn noop() -> Self {
        Metrics::Noop
    }
    pub fn report_event(&self, event: Event) {
        if let Metrics::Prometheus { event_calls, .. } = self {
            if let Some(counter) = event_calls.get(&event) {
                counter.inc();
            }
        }
    }
    pub fn report_event_error(&self, event: Event) {
        if let Metrics::Prometheus { event_errors, .. } = self {
            if let Some(counter) = event_errors.get(&event) {
                counter.inc();
            }
        }
    }
    pub fn update_best_block(&self, number: BlockNumber) {
        if let Metrics::Prometheus { best_block, .. } = self {
            best_block.set(number as u64)
        }
    }
    pub fn update_top_finalized_block(&self, number: BlockNumber) {
        if let Metrics::Prometheus {
            top_finalized_block,
            ..
        } = self
        {
            top_finalized_block.set(number as u64);
        }
    }
}