"""Logger protocol for dependency injection in ApiClient.

This module defines a Protocol that any logger can implement, allowing users
to inject their own logging configuration (JSON, console, custom formatters, etc.).

The protocol is compatible with:
- structlog bound loggers
- stdlib logging.Logger
- Any custom logger with info/debug/warning/error methods

Usage:
    # With structlog (JSON or console based on configuration)
    from alloy_runtime_sdk.logging.config import configure, get_logger, LogConfig, LogFormat
    configure(LogConfig(format=LogFormat.JSON))
    logger = get_logger()

    async with ApiClient(base_url="...", api_key="...", logger=logger) as client:
        await client.list_agents()

    # With stdlib logging
    import logging
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("my_app")

    async with ApiClient(base_url="...", api_key="...", logger=logger) as client:
        await client.list_agents()

    # Without logging (default - uses NullLogger)
    async with ApiClient(base_url="...", api_key="...") as client:
        await client.list_agents()
"""

from typing import Any, Protocol, runtime_checkable

from uuid_extension import uuid7


@runtime_checkable
class ClientLogger(Protocol):
    """Protocol for loggers compatible with ApiClient.

    Any logger implementing these methods can be injected into ApiClient.
    The logger is responsible for its own formatting (JSON, console, etc.).

    All methods accept structured kwargs which will be:
    - Rendered as JSON fields if using a JSON formatter
    - Rendered as key=value pairs if using console formatter
    """

    def info(self, __event: str, **__kwargs: Any) -> None:
        """Log an info-level message with structured data."""
        ...

    def debug(self, __event: str, **__kwargs: Any) -> None:
        """Log a debug-level message with structured data."""
        ...

    def warning(self, __event: str, **__kwargs: Any) -> None:
        """Log a warning-level message with structured data."""
        ...

    def error(self, __event: str, **__kwargs: Any) -> None:
        """Log an error-level message with structured data."""
        ...


class NullLogger:
    """No-op logger implementation.

    Used as the default when no logger is provided to ApiClient.
    All methods are no-ops with minimal overhead.
    """

    __slots__ = ()

    def info(self, _event: str, **_kwargs: Any) -> None:
        """No-op info log."""

    def debug(self, _event: str, **_kwargs: Any) -> None:
        """No-op debug log."""

    def warning(self, _event: str, **_kwargs: Any) -> None:
        """No-op warning log."""

    def error(self, _event: str, **_kwargs: Any) -> None:
        """No-op error log."""


# Singleton instance for efficiency
_null_logger = NullLogger()


def get_null_logger() -> NullLogger:
    """Get the singleton NullLogger instance."""
    return _null_logger


class PipelineLogger:
    __slots__ = ("_logger",)

    def __init__(self, logger: ClientLogger) -> None:
        self._logger = logger

    def info(self, event: str | dict[str, Any], **kwargs: Any) -> None:
        self._log("info", event, **kwargs)

    def debug(self, event: str | dict[str, Any], **kwargs: Any) -> None:
        self._log("debug", event, **kwargs)

    def warning(self, event: str | dict[str, Any], **kwargs: Any) -> None:
        self._log("warning", event, **kwargs)

    def error(self, event: str | dict[str, Any], **kwargs: Any) -> None:
        self._log("error", event, **kwargs)

    def _log(self, level: str, event: str | dict[str, Any], **kwargs: Any) -> None:
        logger_method = getattr(self._logger, level)
        if isinstance(event, dict):
            payload = dict(event)
            payload.update(kwargs)
            event_name = str(payload.pop("event", "pipeline_log"))
            logger_method(event_name, **payload)
            return
        logger_method(event, **kwargs)


def generate_trace_id() -> str:
    """Generate a unique trace ID for cross-system request tracing.

    Returns:
        UUID7 string (e.g., "018f7a3b-1c2d-7e4f-8a9b-0c1d2e3f4a5b")

    This ID is sent as an ``X-Trace-ID`` header to the server and included
    in all SDK log entries for the request, enabling end-to-end correlation
    between SDK and server logs.
    """
    return str(uuid7())
