"""
LLM Service

Service layer for LLM operations with MCP (Model Context Protocol) integration.

v2.0: Includes MCP server integration for HoneyDB tools and event callback system
for real-time tool call visibility.
"""

from typing import Any, Callable

from honeydb_ai.domain.exceptions import APIKeyMissingError, ValidationError
from honeydb_ai.domain.models import Config, LLMResponse, Message
from honeydb_ai.infrastructure.mcp import OpenAIMCPAdapter, AnthropicMCPAdapter, GeminiMCPAdapter
from honeydb_ai.utils.logging import get_logger, log_llm_request, log_llm_response
from honeydb_ai.utils.validators import validate_provider, validate_model_name, validate_temperature, validate_max_tokens
import time


class LLMService:
    """
    Service for LLM operations with MCP integration.

    Provides a unified interface to multiple LLM providers (OpenAI, Anthropic, Gemini)
    with HoneyDB MCP server integration for threat intelligence tools.

    v2.0: MCP integration allows LLMs to autonomously invoke HoneyDB tools.
    """

    def __init__(self, config: Config):
        """
        Initialize LLM service with MCP integration.

        Args:
            config: Application configuration

        Raises:
            ValidationError: If HoneyDB credentials are missing
        """
        self.config = config
        self.logger = get_logger()
        self._adapters: dict[str, OpenAIMCPAdapter | AnthropicMCPAdapter | GeminiMCPAdapter] = {}
        self._validate_mcp_config()
        self._initialize_adapters()

    def _validate_mcp_config(self) -> None:
        """Validate that HoneyDB MCP credentials are configured."""
        if not self.config.honeydb_api_id or not self.config.honeydb_api_key:
            raise ValidationError(
                "HoneyDB API credentials not configured. "
                "Please run 'honeydb-ai configure' to set up your HoneyDB API ID and API key."
            )

    def _initialize_adapters(self) -> None:
        """Initialize MCP-enabled LLM provider adapters based on configuration."""
        # OpenAI with MCP
        if self.config.openai_api_key:
            self._adapters["openai"] = OpenAIMCPAdapter(
                api_key=self.config.openai_api_key,
                mcp_server_url=self.config.mcp_server_url,
                honeydb_api_id=self.config.honeydb_api_id,
                honeydb_api_key=self.config.honeydb_api_key,
            )

        # Anthropic with MCP
        if self.config.anthropic_api_key:
            self._adapters["anthropic"] = AnthropicMCPAdapter(
                api_key=self.config.anthropic_api_key,
                mcp_server_url=self.config.mcp_server_url,
                honeydb_api_id=self.config.honeydb_api_id,
                honeydb_api_key=self.config.honeydb_api_key,
            )

        # Gemini with MCP
        if self.config.gemini_api_key:
            self._adapters["gemini"] = GeminiMCPAdapter(
                api_key=self.config.gemini_api_key,
                mcp_server_url=self.config.mcp_server_url,
                honeydb_api_id=self.config.honeydb_api_id,
                honeydb_api_key=self.config.honeydb_api_key,
            )

        if not self._adapters:
            raise ValidationError(
                "No LLM provider API keys configured. "
                "Please run 'honeydb-ai configure' to set up at least one LLM provider "
                "(OpenAI, Anthropic, or Gemini)."
            )

        self.logger.info(f"Initialized {len(self._adapters)} MCP-enabled LLM provider(s)")

    def get_adapter(self, provider: str | None = None) -> OpenAIMCPAdapter | AnthropicMCPAdapter | GeminiMCPAdapter:
        """
        Get MCP-enabled LLM adapter for a specific provider.

        Args:
            provider: Provider name (openai, anthropic, gemini) or None for default

        Returns:
            MCP adapter instance

        Raises:
            ValidationError: If provider is invalid
            APIKeyMissingError: If API key is not configured
        """
        provider = provider or self.config.default_provider

        # Validate provider
        validate_provider(provider)

        # Check if adapter exists
        if provider not in self._adapters:
            raise APIKeyMissingError(provider)

        return self._adapters[provider]

    async def send_message(
        self,
        messages: list[Message],
        provider: str | None = None,
        model: str | None = None,
        temperature: float | None = None,
        max_tokens: int | None = None,
        event_callback: Callable[[str, dict[str, Any]], None] | None = None,
    ) -> LLMResponse:
        """
        Send messages to an LLM provider with MCP integration.

        The LLM can autonomously invoke HoneyDB tools via the MCP server during
        response generation.

        v2.0: MCP integration + event callback for real-time tool call visibility.

        Args:
            messages: List of messages in the conversation
            provider: Provider to use (None for default from config)
            model: Model to use (None for default from config)
            temperature: Temperature override
            max_tokens: Max tokens override
            event_callback: Optional callback for MCP tool invocation events

        Returns:
            LLMResponse with the assistant's response

        Raises:
            ValidationError: If parameters are invalid
            APIKeyMissingError: If API key is not configured
            Exception: If the API call fails
        """
        # Get provider and model
        provider = provider or self.config.default_provider
        model = model or self.config.default_model
        temperature = temperature if temperature is not None else self.config.temperature
        max_tokens = max_tokens if max_tokens is not None else self.config.max_tokens

        # Validate parameters
        validate_provider(provider)
        validate_model_name(model, provider)
        validate_temperature(temperature)
        validate_max_tokens(max_tokens)

        # Get MCP adapter
        adapter = self.get_adapter(provider)

        # Set event callback if provided
        if event_callback:
            adapter.event_callback = event_callback

        # Convert Message objects to dict format
        message_dicts = [{"role": msg.role, "content": msg.content} for msg in messages]

        # Format messages for provider
        formatted_messages = adapter.format_messages(message_dicts)

        # Log request (metadata only - privacy-safe)
        log_llm_request(self.logger, provider, model, len(messages))

        # Make request
        start_time = time.time()
        try:
            response = await adapter.send_message(
                messages=formatted_messages,
                model=model,
                temperature=temperature,
                max_tokens=max_tokens,
            )

            # Log success
            duration_ms = (time.time() - start_time) * 1000
            log_llm_response(self.logger, provider, model, True, duration_ms)

            return response

        except Exception as e:
            # Log failure
            duration_ms = (time.time() - start_time) * 1000
            log_llm_response(self.logger, provider, model, False, duration_ms, str(e))
            raise

    def list_available_providers(self) -> list[str]:
        """
        List available MCP-enabled providers (those with API keys configured).

        Returns:
            List of provider names
        """
        return list(self._adapters.keys())

    def reload_config(self, config: Config) -> None:
        """
        Reload configuration and reinitialize MCP adapters.

        v2.0 Major Enhancement #8: Configuration hot-reload.

        Args:
            config: New configuration
        """
        self.config = config
        self._adapters.clear()
        self._validate_mcp_config()
        self._initialize_adapters()
        self.logger.info("LLM service configuration reloaded with MCP integration")
