"""
Climeter context detection — auto-detects the caller environment.

Detects whether code is running inside an AI agent (LangChain, CrewAI,
AutoGen, OpenClaw, etc.) or executed by a human, and attempts to extract
a stable session/agent identifier.
"""

import os
import sys


# Environment variable patterns that indicate an AI agent framework
_AGENT_ENV_PATTERNS = [
    # LangChain
    "LANGCHAIN_API_KEY",
    "LANGCHAIN_PROJECT",
    "LANGCHAIN_TRACING_V2",
    "LANGCHAIN_ENDPOINT",
    # CrewAI
    "CREWAI_AGENT_NAME",
    "CREWAI_SESSION_ID",
    # AutoGen
    "AUTOGEN_AGENT_ID",
    "AUTOGEN_SESSION",
    # OpenAI Agents / Assistants
    "OPENAI_ASSISTANT_ID",
    "OPENAI_THREAD_ID",
    # OpenClaw
    "OPENCLAW_SESSION",
    "OPENCLAW_AGENT",
    # Generic agent markers
    "AGENT_SESSION_ID",
    "AGENT_ID",
    "AI_AGENT",
    "CLIMETER_CALLER_TYPE",  # Explicit override
]

# Env vars for extracting a caller ID
_CALLER_ID_ENV_VARS = [
    "CLIMETER_CALLER_ID",
    "AGENT_SESSION_ID",
    "AGENT_ID",
    "OPENCLAW_SESSION",
    "LANGCHAIN_PROJECT",
    "CREWAI_SESSION_ID",
    "AUTOGEN_SESSION",
    "OPENAI_THREAD_ID",
    "OPENAI_ASSISTANT_ID",
]

# Module presence checks — if these are imported, we're likely in an agent
_AGENT_MODULE_MARKERS = [
    "langchain",
    "langchain_core",
    "crewai",
    "autogen",
    "openai_agents",
]


def detect_caller_type() -> str:
    """
    Auto-detect if running inside an AI agent framework.

    Returns:
        'agent'   — running inside a known AI agent framework
        'human'   — running interactively (terminal, stdin is a TTY)
        'unknown' — cannot determine

    Detection order:
    1. Explicit override via CLIMETER_CALLER_TYPE env var
    2. Known agent framework env vars
    3. Imported agent framework modules
    4. TTY detection (human at a terminal)
    """
    # 1. Explicit override
    explicit = os.environ.get("CLIMETER_CALLER_TYPE", "").lower()
    if explicit in ("agent", "human", "unknown"):
        return explicit

    # 2. Agent env vars
    for key in _AGENT_ENV_PATTERNS:
        if os.environ.get(key):
            return "agent"

    # 3. Imported modules (check sys.modules — zero-cost if not imported)
    for module in _AGENT_MODULE_MARKERS:
        if module in sys.modules:
            return "agent"

    # 4. TTY check — if stdin is a terminal, it's a human
    try:
        if sys.stdin.isatty():
            return "human"
    except AttributeError:
        pass

    return "unknown"


def detect_caller_id() -> str:
    """
    Try to extract a stable agent/session identifier from the environment.

    Returns:
        A non-empty string identifier, or empty string if none found.

    Checks (in priority order):
    1. CLIMETER_CALLER_ID — explicit user-provided ID
    2. Known agent session env vars (OpenClaw, LangChain, etc.)
    3. Generic AGENT_SESSION_ID / AGENT_ID
    """
    for key in _CALLER_ID_ENV_VARS:
        value = os.environ.get(key, "").strip()
        if value:
            return value
    return ""


def caller_info() -> dict:
    """
    Convenience: return both caller_type and caller_id as a dict.

    Returns:
        {"caller_type": str, "caller_id": str}

    Example:
        from climeter.context import caller_info
        meter.record("search", price=0.01, **caller_info())
    """
    return {
        "caller_type": detect_caller_type(),
        "caller_id": detect_caller_id(),
    }
