"""
Base LLM Provider Adapter

Abstract base class for all LLM provider implementations.

v2.0 Critical: Includes event callback system for real-time tool call visibility.
"""

from abc import abstractmethod
from datetime import datetime
from typing import Callable

from honeydb_ai.domain.interfaces import LLMProviderAdapter
from honeydb_ai.domain.models import Event, LLMResponse, Message
from honeydb_ai.utils.logging import get_logger


class BaseLLMAdapter(LLMProviderAdapter):
    """
    Base adapter class with common functionality.

    v2.0 Critical: All adapters must emit events via event_callback for tool call visibility.
    """

    def __init__(self, api_key: str):
        """
        Initialize adapter.

        Args:
            api_key: API key for the provider
        """
        self.api_key = api_key
        self.logger = get_logger()

    @abstractmethod
    async def send_message(
        self,
        messages: list[Message],
        model: str | None = None,
        temperature: float | None = None,
        max_tokens: int | None = None,
        stream: bool = False,
        event_callback: Callable[[Event], None] | None = None,
    ) -> LLMResponse:
        """
        Send messages to the LLM provider.

        Must be implemented by subclasses.
        """
        pass

    @abstractmethod
    def validate_api_key(self) -> bool:
        """Validate API key."""
        pass

    @abstractmethod
    def get_provider_name(self) -> str:
        """Get provider name."""
        pass

    @abstractmethod
    def get_available_models(self) -> list[str]:
        """Get available models."""
        pass

    # Helper methods for event emission

    def _emit_event(
        self,
        event_callback: Callable[[Event], None] | None,
        event_type: str,
        data: dict
    ) -> None:
        """
        Emit an event via the callback.

        Args:
            event_callback: Optional event callback
            event_type: Type of event
            data: Event data
        """
        if event_callback:
            event = Event(
                type=event_type,  # type: ignore
                timestamp=datetime.now(),
                data=data
            )
            try:
                event_callback(event)
            except Exception as e:
                self.logger.warning(f"Event callback failed: {e}")

    def _emit_request_started(
        self,
        event_callback: Callable[[Event], None] | None,
        model: str,
        message_count: int
    ) -> None:
        """
        Emit request_started event.

        Args:
            event_callback: Optional event callback
            model: Model identifier
            message_count: Number of messages in request
        """
        self._emit_event(
            event_callback,
            "request_started",
            {
                "provider": self.get_provider_name(),
                "model": model,
                "message_count": message_count,
            }
        )

    def _emit_tool_call(
        self,
        event_callback: Callable[[Event], None] | None,
        tool_name: str,
        tool_args: dict | None = None
    ) -> None:
        """
        Emit tool_call event.

        v2.0 Critical: Real-time visibility into MCP tool invocations.

        Args:
            event_callback: Optional event callback
            tool_name: Name of the tool being called
            tool_args: Tool arguments (optional)
        """
        data = {"tool_name": tool_name}
        if tool_args:
            # Don't include sensitive args
            safe_args = {k: v for k, v in tool_args.items() if k not in ['api_key', 'token', 'password']}
            data["tool_args"] = safe_args

        self._emit_event(event_callback, "tool_call", data)

    def _emit_tool_result(
        self,
        event_callback: Callable[[Event], None] | None,
        tool_name: str,
        success: bool,
        error: str | None = None
    ) -> None:
        """
        Emit tool_result event.

        Args:
            event_callback: Optional event callback
            tool_name: Name of the tool
            success: Whether tool call succeeded
            error: Error message if failed
        """
        data = {
            "tool_name": tool_name,
            "success": success,
        }
        if error:
            data["error"] = error

        self._emit_event(event_callback, "tool_result", data)

    def _emit_response_received(
        self,
        event_callback: Callable[[Event], None] | None,
        finish_reason: str | None = None,
        duration_ms: float | None = None
    ) -> None:
        """
        Emit response_received event.

        Args:
            event_callback: Optional event callback
            finish_reason: Reason the response finished
            duration_ms: Request duration in milliseconds
        """
        data = {"provider": self.get_provider_name()}
        if finish_reason:
            data["finish_reason"] = finish_reason
        if duration_ms:
            data["duration_ms"] = duration_ms

        self._emit_event(event_callback, "response_received", data)

    # Helper methods for message conversion

    def _messages_to_dict(self, messages: list[Message]) -> list[dict]:
        """
        Convert domain Message objects to provider-specific dict format.

        Args:
            messages: List of Message objects

        Returns:
            List of message dicts suitable for API calls
        """
        return [
            {
                "role": msg.role,
                "content": msg.content,
            }
            for msg in messages
        ]
