"""OpenTelemetry helpers for MCP."""

from __future__ import annotations

from collections.abc import Generator, Mapping
from contextlib import contextmanager
from typing import Any

from opentelemetry.context import Context
from opentelemetry.propagate import extract, inject
from opentelemetry.trace import SpanKind, get_current_span, get_tracer
from opentelemetry.trace.span import Span

_tracer = get_tracer("mcp-python-sdk")


@contextmanager
def otel_span(
    name: str,
    *,
    kind: SpanKind,
    attributes: dict[str, Any] | None = None,
    context: Context | None = None,
    record_exception: bool = True,
    set_status_on_exception: bool = True,
) -> Generator[Span]:
    """Create an OTel span."""
    with _tracer.start_as_current_span(
        name,
        kind=kind,
        attributes=attributes,
        context=context,
        record_exception=record_exception,
        set_status_on_exception=set_status_on_exception,
    ) as span:
        yield span


def inject_trace_context(meta: dict[str, Any]) -> None:
    """Inject W3C trace context (traceparent/tracestate) into a `_meta` dict."""
    inject(meta)


def extract_trace_context(meta: Mapping[str, Any] | None) -> Context | None:
    """Extract W3C trace context from a `_meta` dict.

    Returns `None` when the carrier is absent, malformed, or carries no
    valid `traceparent`, so callers fall through to ambient parenting; an
    explicit empty `Context` would orphan the span instead of nesting under
    the current one.
    """
    if not meta:
        return None
    try:
        ctx = extract(meta)
    except (ValueError, TypeError):
        return None
    if not get_current_span(ctx).get_span_context().is_valid:
        return None
    return ctx
