import asyncio
import logging
from functools import wraps

import httpx

logger = logging.getLogger(__name__)

# Hold references to background tasks so they aren't garbage collected mid-execution
_background_tasks: set[asyncio.Task] = set()


class APIError(Exception):
    """API error with user-friendly message."""


def handle_api_errors(func):
    """Decorator to catch API errors and return user-friendly messages."""

    @wraps(func)
    async def wrapper(*args, **kwargs):
        try:
            return await func(*args, **kwargs)
        except APIError as e:
            return str(e)
        except httpx.HTTPStatusError as e:
            return f"Error: API request failed ({e.response.status_code})"
        except httpx.RequestError as e:
            return f"Error: Could not connect to API ({e})"

    return wrapper


def track_tool(client_fn, metric_slug="mcp_tool_call"):
    """Decorator that tracks tool calls as metric events.

    Fires a background POST to /v1/metrics/track after the tool runs.
    Extracts topic_id, knowledge_id etc. from the tool's kwargs.

    Args:
        client_fn: Callable that returns the ApiClient (deferred to avoid import order issues).
        metric_slug: The metric slug to track against.
    """

    # Only these kwargs become tags — prevents user content from leaking into metrics
    INCLUDE_KWARGS = {"topic_id", "knowledge_id", "name"}

    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            result = await func(*args, **kwargs)

            tags = {"tool_name": func.__name__}
            for key, value in kwargs.items():
                if key in INCLUDE_KWARGS and value is not None:
                    tags[key] = str(value)

            async def _send():
                try:
                    from . import routes

                    await client_fn().post(
                        routes.track_metric(),
                        {"metric_slug": metric_slug, "tags": tags},
                    )
                except Exception:
                    logger.warning("Failed to track metric for %s", func.__name__, exc_info=True)

            task = asyncio.create_task(_send())
            _background_tasks.add(task)
            task.add_done_callback(_background_tasks.discard)
            return result

        return wrapper

    return decorator
