use crate::observability::config::{OtlpProtocol, TraceConfig};
use crate::observability::filter::SharedOrderedFilter;
use crate::observability::otlp_http::build_no_proxy_http_client;
use crate::observability::remote::{append_otlp_path, resource};
use anyhow::{bail, Context as AnyhowContext, Result};
use opentelemetry::trace::TracerProvider;
use opentelemetry_otlp::{Protocol, SpanExporter, WithExportConfig, WithHttpConfig};
use opentelemetry_sdk::trace::SdkTracerProvider;
use tracing_subscriber::{Layer, Registry};

/// Builds the OpenTelemetry tracing layer and provider.
pub fn build_trace_layer(
    config: &TraceConfig,
    filter: SharedOrderedFilter,
) -> Result<(Box<dyn Layer<Registry> + Send + Sync>, SdkTracerProvider)> {
    if config.exporter != "otlp" {
        bail!("unsupported trace exporter `{}`", config.exporter);
    }

    let exporter = match config.protocol {
        OtlpProtocol::HttpBinary => SpanExporter::builder()
            .with_http()
            .with_http_client(build_no_proxy_http_client()?)
            .with_protocol(Protocol::HttpBinary)
            .with_endpoint(append_otlp_path(&config.endpoint, "/v1/traces"))
            .build(),
        OtlpProtocol::Grpc => SpanExporter::builder()
            .with_tonic()
            .with_endpoint(config.endpoint.clone())
            .build(),
    }
    .context("failed to create OpenTelemetry trace exporter")?;

    let provider = SdkTracerProvider::builder()
        .with_batch_exporter(exporter)
        .with_resource(resource(config))
        .build();

    let tracer = provider.tracer("pi_logger");
    let layer = tracing_opentelemetry::layer()
        .with_tracer(tracer)
        .with_filter(filter)
        .boxed();

    Ok((layer, provider))
}
