"""AgentCost - Know what your AI agents actually cost."""

import contextvars
import functools
import os
import time
from typing import Optional, Callable, Any

from .client import EventClient
from .event import CostEvent
from .pricing import PricingDB
from .tracker import Tracker, _active_run

__version__ = "0.1.0"

# Global instance set by init()
_global_instance: Optional["AgentCost"] = None


class AgentCost:
    """Main entry point for the AgentCost SDK.

    Usage:
        ac = AgentCost(api_key="ac_xxxxx")
        # Or set AGENTCOST_API_KEY env var and omit api_key:
        ac = AgentCost()

        # Local/debug mode — prints events to stderr, no backend needed:
        ac = AgentCost(debug=True)

        # Option 1: Context manager for a run
        with ac.track_run("my-agent", task="process_refund") as run:
            run.record("stripe_api", cost=0.02)

        # Option 2: Decorator for tools
        @ac.track_tool(name="salesforce_api", cost_per_call=0.01)
        def search_salesforce(query):
            ...

        # Option 3: LangChain integration
        from agentcost.integrations.langchain import CostTracker
        tracker = CostTracker(ac, agent_name="my-agent")
        agent.invoke(input, config={"callbacks": [tracker]})
    """

    def __init__(
        self,
        api_key: Optional[str] = None,
        api_url: str = "https://agentmeter.com/api/v1",
        pricing_overrides: Optional[dict] = None,
        debug: bool = False,
    ):
        from .credentials import resolve_api_key
        resolved_key = resolve_api_key(api_key) or ""
        if not resolved_key and not debug:
            raise ValueError(
                "No API key found. Run `agentcost login` or set AGENTCOST_API_KEY env var. "
                "For local testing, use AgentCost(debug=True)."
            )

        self._api_key = resolved_key
        self._debug = debug
        self._client = EventClient(
            api_key=resolved_key, api_url=api_url, debug=debug
        )
        self._pricing = PricingDB(overrides=pricing_overrides)

    def tracker(
        self, agent_id: str, **default_metadata
    ) -> Tracker:
        """Create a tracker for a specific agent."""
        return Tracker(
            client=self._client,
            pricing=self._pricing,
            agent_id=agent_id,
            default_metadata=default_metadata,
        )

    def track_run(self, agent_id: str, task: Optional[str] = None, **metadata):
        """Convenience: directly create a run context manager.

        Usage:
            with ac.track_run("my-agent", task="refund") as run:
                run.record("stripe_api", cost=0.02)
        """
        t = self.tracker(agent_id, **metadata)
        return t.run(task=task)

    def track_tool(
        self,
        name: Optional[str] = None,
        cost_per_call: Optional[float] = None,
        agent_id: str = "default",
    ) -> Callable:
        """Decorator to track cost of a tool function.

        When called inside a track_run() context, events are automatically
        linked to the active run. Otherwise they are standalone events.

        Usage:
            @ac.track_tool(name="salesforce_api", cost_per_call=0.01)
            def search_salesforce(query):
                ...
        """

        def decorator(func: Callable) -> Callable:
            tool_name = name or func.__name__
            if cost_per_call is not None:
                self._pricing.set_tool_price(tool_name, cost_per_call)

            @functools.wraps(func)
            def wrapper(*args: Any, **kwargs: Any) -> Any:
                start = time.time()
                result = func(*args, **kwargs)
                duration_ms = int((time.time() - start) * 1000)

                # Pick up the active run context if one exists
                active = _active_run.get(None)
                effective_agent = active._agent_id if active else agent_id
                effective_run_id = active._run_id if active else None

                event = CostEvent(
                    agent_id=effective_agent,
                    run_id=effective_run_id,
                    component_name=tool_name,
                    event_type="tool_call",
                    cost_usd=self._pricing.tool_cost(tool_name),
                    duration_ms=duration_ms,
                )

                if active:
                    active._events.append(event)

                self._client.enqueue(event)
                return result

            return wrapper

        return decorator

    @property
    def pricing(self) -> PricingDB:
        """Access the pricing database for manual configuration."""
        return self._pricing

    def flush(self) -> None:
        """Force flush all pending events."""
        self._client._flush()

    def shutdown(self) -> None:
        """Shutdown the client and flush remaining events."""
        self._client.shutdown()


def init(
    api_key: Optional[str] = None,
    agent_id: str = "default",
    api_url: str = "https://agentmeter.com/api/v1",
    debug: bool = False,
) -> AgentCost:
    """Initialize AgentCost and auto-patch all detected LLM providers.

    This is the simplest way to use AgentCost. Call this once at startup
    and all OpenAI/Anthropic calls are automatically tracked.

    Usage:
        import agentcost
        agentcost.init(api_key="ac_xxxxx", agent_id="my-agent")

        # Or just set AGENTCOST_API_KEY env var:
        agentcost.init(agent_id="my-agent")

        # That's it — all LLM calls are now tracked.

    Args:
        api_key: Your AgentCost API key (or set AGENTCOST_API_KEY env var).
        agent_id: Name for this agent in the dashboard.
        api_url: API endpoint (default: agentmeter.com).
        debug: If True, prints events to stderr instead of sending to API.

    Returns:
        The AgentCost instance (for advanced usage like track_run/track_tool).
    """
    global _global_instance

    from .patch import patch_all

    _global_instance = AgentCost(
        api_key=api_key, api_url=api_url, debug=debug
    )
    patched = patch_all(_global_instance, agent_id)

    if debug:
        import sys
        if patched:
            print(f"[agentcost] Auto-patched: {', '.join(patched)}", file=sys.stderr)
        else:
            print("[agentcost] No providers detected (install openai or anthropic)", file=sys.stderr)

    return _global_instance


def shutdown() -> None:
    """Shutdown the global AgentCost instance and restore original methods."""
    global _global_instance

    from .patch import unpatch_all

    unpatch_all()
    if _global_instance:
        _global_instance.shutdown()
        _global_instance = None
