"""
ACP SDK Client
==============
Wraps OpenAI, Anthropic, or Google clients.
Intercepts calls, ships telemetry asynchronously.
"""
import time
import uuid
from typing import Any, Optional

from .config import ACPConfig
from .telemetry.buffer import TelemetryBuffer

PRICING = {
    "gpt-4o":                     (0.0025,  0.01),
    "gpt-4o-mini":                (0.00015, 0.0006),
    "gpt-3.5-turbo":              (0.0005,  0.0015),
    "claude-3-5-sonnet-20241022": (0.003,   0.015),
    "claude-3-5-haiku-20241022":  (0.001,   0.005),
    "claude-3-opus-20240229":     (0.015,   0.075),
}


def _compute_cost(model: str, in_tok: int, out_tok: int) -> float:
    p = PRICING.get(model, (0.003, 0.015))
    return (in_tok / 1000) * p[0] + (out_tok / 1000) * p[1]


def wrap(client, api_key: str = None, app: str = "default",
         environment: str = "production", config: ACPConfig = None,
         tags: dict = None, **kwargs) -> "ACPClient":
    """
    Wrap an OpenAI or Anthropic client with ACP observability.

    Args:
        client:      OpenAI() or anthropic.Anthropic() instance
        api_key:     ACP API key (or set ACP_API_KEY env var)
        app:         Application name for attribution
        environment: production | staging | development
        config:      ACPConfig object (overrides individual params)
        tags:        Custom tags dict

    Returns:
        ACPClient proxy with identical interface to original client
    """
    if config is None:
        config = ACPConfig(
            api_key=api_key or "",
            app=app,
            environment=environment,
            tags=tags or {},
        )
        if not config.api_key:
            import os
            config.api_key = os.getenv("ACP_API_KEY", "")
    return ACPClient(client, config)


class ACPClient:
    """Transparent proxy around an LLM client with telemetry."""

    def __init__(self, underlying, config: ACPConfig):
        object.__setattr__(self, "_underlying", underlying)
        object.__setattr__(self, "_config", config)
        object.__setattr__(self, "_buffer", TelemetryBuffer(
            api_key=config.api_key,
            ingest_url=config.ingest_url,
        ))

        # Detect provider
        cls_name = type(underlying).__module__
        if "openai" in cls_name:
            provider = "openai"
        elif "anthropic" in cls_name:
            provider = "anthropic"
        else:
            provider = "unknown"
        object.__setattr__(self, "_provider", provider)

        # Wrap sub-objects
        object.__setattr__(self, "chat", _ChatProxy(self))
        object.__setattr__(self, "messages", _MessagesProxy(self))
        object.__setattr__(self, "embeddings", _EmbeddingsProxy(self))

    def __getattr__(self, name):
        return getattr(object.__getattribute__(self, "_underlying"), name)

    def flush(self, timeout: float = 5.0):
        """Force-flush pending telemetry. Call before process exit."""
        object.__getattribute__(self, "_buffer").flush(timeout)

    def stats(self) -> dict:
        return object.__getattribute__(self, "_buffer").stats()

    def _emit(self, event: dict):
        config = object.__getattribute__(self, "_config")
        buf    = object.__getattribute__(self, "_buffer")
        event["app"]         = config.app
        event["environment"] = config.environment
        if config.tags:
            event.setdefault("tags", {}).update(config.tags)
        buf.enqueue(event)


class _ChatProxy:
    def __init__(self, acp: ACPClient):
        self._acp = acp

    @property
    def completions(self):
        return _CompletionsProxy(self._acp)


class _CompletionsProxy:
    def __init__(self, acp: ACPClient):
        self._acp = acp

    def create(self, *args, **kwargs):
        return self._acp._call_openai("chat", *args, **kwargs)

    async def acreate(self, *args, **kwargs):
        return await self._acp._async_call_openai("chat", *args, **kwargs)


class _MessagesProxy:
    """Anthropic messages.create()"""
    def __init__(self, acp: ACPClient):
        self._acp = acp

    def create(self, *args, **kwargs):
        return self._acp._call_anthropic(*args, **kwargs)


class _EmbeddingsProxy:
    def __init__(self, acp: ACPClient):
        self._acp = acp

    def create(self, *args, **kwargs):
        return self._acp._call_embeddings(*args, **kwargs)


# ── Call interceptors ──────────────────────────────────────────────────────────

def _extract_acp_params(kwargs: dict) -> dict:
    """Pop ACP-specific params from kwargs without passing them to provider."""
    return {
        "user_id":    kwargs.pop("_acp_user_id", None),
        "session_id": kwargs.pop("_acp_session_id", None),
        "agent_id":   kwargs.pop("_acp_agent_id", None),
        "run_id":     kwargs.pop("_acp_run_id", None),
        "tags":       kwargs.pop("_acp_tags", {}),
    }


def _patch_acpclient():
    """Add call methods to ACPClient."""

    def _call_openai(self, endpoint, *args, **kwargs):
        acp  = _extract_acp_params(kwargs)
        underlying = object.__getattribute__(self, "_underlying")
        model = kwargs.get("model", "unknown")
        t0   = time.time()
        try:
            resp = underlying.chat.completions.create(*args, **kwargs)
            latency = int((time.time()-t0)*1000)
            usage   = resp.usage if resp.usage else type("u", (), {"prompt_tokens":0,"completion_tokens":0})()
            cost    = _compute_cost(model, usage.prompt_tokens, usage.completion_tokens)
            self._emit({
                "request_id":   str(uuid.uuid4()),
                "provider":     "openai",
                "model_id":     model,
                "input_tokens": usage.prompt_tokens,
                "output_tokens":usage.completion_tokens,
                "cost_usd":     cost,
                "latency_ms":   latency,
                "status":       "success",
                **acp,
            })
            return resp
        except Exception as e:
            latency = int((time.time()-t0)*1000)
            self._emit({
                "request_id":  str(uuid.uuid4()),
                "provider":    "openai",
                "model_id":    model,
                "input_tokens":0, "output_tokens":0, "cost_usd":0,
                "latency_ms":  latency,
                "status":      "error",
                "error_message": str(e)[:500],
                **acp,
            })
            raise

    def _call_anthropic(self, *args, **kwargs):
        acp   = _extract_acp_params(kwargs)
        underlying = object.__getattribute__(self, "_underlying")
        model = kwargs.get("model", "unknown")
        t0    = time.time()
        try:
            resp    = underlying.messages.create(*args, **kwargs)
            latency = int((time.time()-t0)*1000)
            usage   = resp.usage
            cost    = _compute_cost(model, usage.input_tokens, usage.output_tokens)
            self._emit({
                "request_id":   str(uuid.uuid4()),
                "provider":     "anthropic",
                "model_id":     model,
                "input_tokens": usage.input_tokens,
                "output_tokens":usage.output_tokens,
                "cost_usd":     cost,
                "latency_ms":   latency,
                "status":       "success",
                **acp,
            })
            return resp
        except Exception as e:
            latency = int((time.time()-t0)*1000)
            self._emit({
                "request_id": str(uuid.uuid4()),
                "provider": "anthropic", "model_id": model,
                "input_tokens":0,"output_tokens":0,"cost_usd":0,
                "latency_ms": latency, "status": "error",
                "error_message": str(e)[:500], **acp,
            })
            raise

    def _call_embeddings(self, *args, **kwargs):
        acp   = _extract_acp_params(kwargs)
        underlying = object.__getattribute__(self, "_underlying")
        model = kwargs.get("model", "text-embedding-3-small")
        t0    = time.time()
        resp  = underlying.embeddings.create(*args, **kwargs)
        usage = resp.usage
        cost  = _compute_cost(model, usage.prompt_tokens, 0)
        self._emit({
            "request_id":"embeddings-"+str(uuid.uuid4())[:8],
            "provider":"openai","model_id":model,
            "input_tokens":usage.prompt_tokens,"output_tokens":0,
            "cost_usd":cost,"latency_ms":int((time.time()-t0)*1000),
            "status":"success",**acp,
        })
        return resp

    ACPClient._call_openai    = _call_openai
    ACPClient._call_anthropic = _call_anthropic
    ACPClient._call_embeddings = _call_embeddings

_patch_acpclient()
