//! Shared utilities for Signet services.

#![warn(
    missing_copy_implementations,
    missing_debug_implementations,
    missing_docs,
    unreachable_pub,
    clippy::missing_const_for_fn,
    rustdoc::all
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![deny(unused_must_use, rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

use crate::utils::{
    from_env::{FromEnv, FromEnvErr},
    metrics::MetricsConfig,
    otlp::OtelGuard,
    tracing::TracingConfig,
};

#[cfg(feature = "perms")]
/// Permissioning and authorization utilities for Signet builders.
pub mod perms;

/// Signet utilities.
pub mod utils {
    /// Slot calculator for determining the current slot and timepoint within a
    /// slot.
    pub mod calc;

    /// [`FromEnv`], [`FromEnvVar`] traits and related utilities.
    ///
    /// [`FromEnv`]: from_env::FromEnv
    /// [`FromEnvVar`]: from_env::FromEnvVar
    pub mod from_env;

    /// Prometheus metrics utilities.
    pub mod metrics;

    /// Axum OpenTelemetry utilities.
    pub mod otel_axum;

    /// OpenTelemetry utilities.
    pub mod otlp;

    #[cfg(feature = "alloy")]
    /// Alloy Provider configuration and instantiation
    pub mod provider;

    #[cfg(feature = "aws")]
    /// Signer using a local private key or AWS KMS key.
    pub mod signer;

    /// Tracing utilities.
    pub mod tracing;

    /// Block watcher utilities.
    #[cfg(feature = "block_watcher")]
    pub mod block_watcher;
}

/// Re-exports of common dependencies.
pub mod deps {
    pub use metrics;
    pub use opentelemetry;
    pub use opentelemetry_otlp;
    pub use opentelemetry_sdk;
    pub use tracing;
    pub use tracing_core;
    pub use tracing_opentelemetry;
    pub use tracing_subscriber;
}

/// Init metrics and tracing, including OTLP if enabled.
///
/// This will perform the following:
/// - Read environment configuration for tracing
/// - Determine whether to enable OTLP
/// - Install a global tracing subscriber, using the OTLP provider if enabled
/// - Read environment configuration for metrics
/// - Install a global metrics recorder and serve it over HTTP on 0.0.0.0
///
/// See [`init_tracing`] and [`init_metrics`] for more
/// details on specific actions taken and env vars read.
///
/// # Returns
///
/// The OpenTelemetry guard, if OTLP is enabled. This guard should be kept alive
/// for the lifetime of the program to ensure the exporter continues to send
/// data to the remote API.
///
/// [`init_tracing`]: utils::tracing::init_tracing
/// [`init_metrics`]: utils::metrics::init_metrics
#[deprecated(since = "0.18.0-rc.11", note = "use `init` instead")]
pub fn init4() -> Option<OtelGuard> {
    let guard = utils::tracing::init_tracing();
    utils::metrics::init_metrics();

    // This will install the AWS-LC-Rust TLS provider for rustls, if no other
    // provider has been installed yet
    #[cfg(feature = "rustls")]
    let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();

    guard
}

/// Trait for config types that can be used with [`init`].
///
/// Implementors must provide access to [`TracingConfig`] and [`MetricsConfig`],
/// and must be loadable from the environment via [`FromEnv`].
///
/// # Example
///
/// ```ignore
/// #[derive(Debug, FromEnv)]
/// pub struct MyConfig {
///     pub tracing: TracingConfig,
///     pub metrics: MetricsConfig,
///     #[from_env(var = "MY_THING", desc = "some app-specific value")]
///     pub my_thing: String,
/// }
///
/// impl Init4Config for MyConfig {
///     fn tracing(&self) -> &TracingConfig { &self.tracing }
///     fn metrics(&self) -> &MetricsConfig { &self.metrics }
/// }
/// ```
pub trait Init4Config: FromEnv {
    /// Get the tracing configuration.
    fn tracing(&self) -> &TracingConfig;
    /// Get the metrics configuration.
    fn metrics(&self) -> &MetricsConfig;
}

/// The result of [`init`]: the loaded config and an optional OTLP guard.
///
/// The [`OtelGuard`] (if present) must be kept alive for the lifetime of the
/// program to ensure the OTLP exporter continues to send data.
#[derive(Debug)]
pub struct ConfigAndGuard<T> {
    /// The loaded configuration.
    pub config: T,
    /// The OTLP guard, if OTLP was enabled.
    pub guard: Option<OtelGuard>,
}

/// Load config from the environment and initialize metrics and tracing.
///
/// This will perform the following:
/// - Load `T` from environment variables via [`FromEnv`]
/// - Read tracing configuration from the loaded config
/// - Determine whether to enable OTLP
/// - Install a global tracing subscriber, using the OTLP provider if enabled
/// - Read metrics configuration from the loaded config
/// - Install a global metrics recorder and serve it over HTTP on 0.0.0.0
///
/// See [`init_tracing`] and [`init_metrics`] for more
/// details on specific actions taken and env vars read.
///
/// [`init_tracing`]: utils::tracing::init_tracing
/// [`init_metrics`]: utils::metrics::init_metrics
pub fn init<T: Init4Config>() -> Result<ConfigAndGuard<T>, FromEnvErr> {
    let config = T::from_env()?;

    let guard = utils::tracing::init_tracing_with_config(config.tracing().clone());
    utils::metrics::init_metrics_with_config(*config.metrics());

    // This will install the AWS-LC-Rust TLS provider for rustls, if no other
    // provider has been installed yet
    #[cfg(feature = "rustls")]
    let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();

    Ok(ConfigAndGuard { config, guard })
}
