"""
DSPy integration for Risicare SDK.

Registers a BaseCallback that creates spans for DSPy module invocations,
LM calls, and tool calls. Uses call_id-based span tracking with memory guard.

Span Hierarchy:
    dspy.module/{module_type}          [INTERNAL, span_kind="module"]
      dspy.lm/{model}                  [LLM_CALL, gen_ai.system from model prefix]
        (provider span suppressed)
      dspy.tool/{tool_name}            [TOOL_CALL]

Suppresses provider instrumentation during LM calls to avoid duplicates.

Usage (automatic — zero config):
    import risicare
    risicare.init(api_key="rsk-...")
    import dspy
    dspy.configure(lm=dspy.LM("openai/gpt-4o"))
    cot = dspy.ChainOfThought("question -> answer")
    result = cot(question="What is 2+2?")  # Traced
"""

from __future__ import annotations

import logging
import threading
from typing import Any

logger = logging.getLogger(__name__)

_instrumented = False
_lock = threading.Lock()


def instrument_dspy(module: Any) -> None:
    """
    Apply instrumentation to DSPy module.

    Called by the import hook system when `dspy` is imported.
    """
    global _instrumented
    if _instrumented:
        return

    with _lock:
        if _instrumented:
            return
        try:
            from risicare.integrations._base import check_version_compatibility

            check_version_compatibility("dspy")

            from risicare.integrations.dspy._callback import register_callback

            register_callback(module)
            _instrumented = True
            logger.debug("Instrumented DSPy")
        except Exception as e:
            logger.debug(f"Failed to instrument DSPy: {e}")
