"""OTel SDK compatibility patches.

- flags patch: Force span flags=0 for Tencent APM parent_span_id detection.
- detach patch: Suppress ValueError when detaching tokens across async contexts.
"""
from __future__ import annotations


def apply_compat_patches() -> None:
    """Apply OTel SDK compatibility patches. Call before any span export."""
    _patch_span_flags()
    _patch_context_detach()


def _patch_span_flags() -> None:
    """Force OTLP encoder to emit flags=0.

    Python OTel SDK 1.27+ encodes non-zero flags on OTLP spans.
    Tencent APM silently drops parent_span_id when flags != 0.
    """
    try:
        from opentelemetry.exporter.otlp.proto.common._internal import (
            trace_encoder as _enc,
        )
        _enc._span_flags = lambda _parent_span_context: 0  # type: ignore[assignment]
    except ImportError:
        pass


def _patch_context_detach() -> None:
    """Make context.detach() tolerant of cross-context token resets.

    Problem: OpenInference instrumentors wrap async generators and call
    context_api.detach(token) in their finally block. When the generator
    is closed via aclose() from a different async task (e.g. adapter's
    streaming loop cancels the generator on client disconnect), the
    ContextVar.reset(token) raises ValueError because the token was
    created in a different execution context.

    This is harmless — the context will be garbage-collected anyway —
    but produces noisy "Failed to detach context" tracebacks in logs.

    Fix: patch ContextVarsRuntimeContext.detach to suppress the ValueError.
    """
    try:
        from opentelemetry.context.contextvars_context import (
            ContextVarsRuntimeContext,
        )
    except ImportError:
        return

    if getattr(ContextVarsRuntimeContext, "_eo_detach_patched", False):
        return

    _orig_detach = ContextVarsRuntimeContext.detach

    def _safe_detach(self, token):  # type: ignore[no-untyped-def]
        try:
            return _orig_detach(self, token)
        except (ValueError, RuntimeError):
            # ValueError: token was created in a different Context — happens
            #   when an async generator is closed from a different task.
            # RuntimeError: token has already been used — happens when detach
            #   is called twice (e.g. generator finally + outer finally).
            # Both are harmless cleanup failures, suppress silently.
            pass

    ContextVarsRuntimeContext.detach = _safe_detach  # type: ignore[assignment]
    ContextVarsRuntimeContext._eo_detach_patched = True  # type: ignore[attr-defined]
