use crate::auth_token::AuthToken;
use crate::otel::auth_interceptor::AuthInterceptor;
use crate::otel::config::RESOURCE_ATTRIBUTES;
use crate::otel::runtime::tokio_runtime;
use opentelemetry_otlp::{LogExporter, WithExportConfig, WithTonicConfig};
use opentelemetry_sdk::{Resource, logs};
use std::sync::{Arc, RwLock};

pub fn init_otel_logs(
    auth_token: Arc<AuthToken>,
    endpoint: &str,
) -> anyhow::Result<logs::SdkLoggerProvider> {
    let rt = tokio_runtime();

    // Build the OTLP log exporter and logger provider inside the Tokio runtime.
    let logger_provider = rt.block_on(async {
        // 1) OTLP log exporter (gRPC to your collector)
        let log_exporter = LogExporter::builder()
            .with_tonic() // gRPC client
            .with_endpoint(endpoint)
            .with_interceptor(AuthInterceptor { token: auth_token }) // <-- dynamic header injection
            .build()?; // this is where "no reactor" would occur

        // 2) Resource attributes
        let resource = Resource::builder_empty()
            .with_attributes(RESOURCE_ATTRIBUTES.iter().cloned())
            .build();

        // 3) Logger provider with batch exporter
        let logger_provider = logs::SdkLoggerProvider::builder()
            .with_batch_exporter(log_exporter)
            .with_resource(resource)
            .build();

        Ok::<logs::SdkLoggerProvider, anyhow::Error>(logger_provider)
    })?;

    Ok(logger_provider)
}

/// A simple manager that holds a current logger provider and can rebuild it when the API token changes.
/// opentelemetry-otlp's exporter metadata is configured at build-time; there isn't a runtime method to mutate it.
/// The recommended approach is to recreate the exporter/provider when credentials change.
#[derive(Clone, Debug)]
pub struct LoggerProviderManager {
    inner: Arc<RwLock<logs::SdkLoggerProvider>>, // current provider
    endpoint: String,
}

impl LoggerProviderManager {
    /// Create a new manager from an initial provider.
    pub fn new(initial_provider: logs::SdkLoggerProvider, endpoint: &str) -> Self {
        Self {
            inner: Arc::new(RwLock::new(initial_provider)),
            endpoint: endpoint.to_string(),
        }
    }

    pub fn create_from_token(token: Arc<AuthToken>, endpoint: &str) -> anyhow::Result<Self> {
        let provider = init_otel_logs(token, endpoint)?;
        Ok(Self {
            inner: Arc::new(RwLock::new(provider)),
            endpoint: endpoint.to_string(),
        })
    }

    /// Get a clone of the current provider (read-only usage).
    pub fn get(&self) -> logs::SdkLoggerProvider {
        self.inner
            .read()
            .expect("Failed to acquire lock on SdkLoggerProvider")
            .clone()
    }

    pub fn shutdown(&self) {
        let guard = self.inner.write().unwrap();
        let _ = guard.shutdown();
    }
}
