"""TraceContextExtractor for extracting trace context from the active OTel span.

Extracts TraceId, SpanId, and TraceFlags from the current OpenTelemetry span
context. Returns None when no active span exists or the OTel SDK is unavailable.

Format requirements (per OTel spec):
    TraceId:    32-character lowercase hexadecimal string
    SpanId:     16-character lowercase hexadecimal string
    TraceFlags:  2-character zero-padded hexadecimal string
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional


@dataclass
class TraceContext:
    """Trace context extracted from an active OpenTelemetry span.

    Attributes:
        trace_id:    32-char lowercase hex string.
        span_id:     16-char lowercase hex string.
        trace_flags:  2-char zero-padded hex string.
    """

    trace_id: str
    span_id: str
    trace_flags: str


class TraceContextExtractor:
    """Extracts trace context from the active OpenTelemetry span."""

    @staticmethod
    def extract() -> Optional[TraceContext]:
        """Extract trace context from the active OTel span.

        Uses ``opentelemetry.trace.get_current_span()`` to obtain the active
        span, then reads its SpanContext for TraceId, SpanId, and TraceFlags.

        Returns:
            A :class:`TraceContext` if a valid, recording span is active,
            or ``None`` if no active span exists, the span context is invalid,
            or the OpenTelemetry SDK is not installed.
        """
        try:
            from opentelemetry import trace

            span = trace.get_current_span()

            # INVALID_SPAN or non-recording spans have no useful context
            if span is None or not span.is_recording():
                return None

            ctx = span.get_span_context()
            if ctx is None or not ctx.is_valid:
                return None

            # Format per OTel spec: lowercase hex, fixed width
            trace_id = format(ctx.trace_id, "032x")
            span_id = format(ctx.span_id, "016x")
            trace_flags = format(ctx.trace_flags, "02x")

            return TraceContext(
                trace_id=trace_id,
                span_id=span_id,
                trace_flags=trace_flags,
            )
        except Exception:
            # Gracefully handle missing SDK or any unexpected errors
            return None
