"""
LLM adapter interface for pluggable model providers.

Implements abstraction layer for LLM integration, supporting
multiple providers (OpenAI, Anthropic, local models, etc.).
"""

from abc import ABC, abstractmethod
from typing import Dict, Any

from markitect.prompts.execution.models import RunConfig, LLMResponse


class LLMAdapter(ABC):
    """
    Abstract base class for LLM providers.

    Enables pluggable LLM backends without prescribing implementation.
    Implementations can wrap OpenAI, Anthropic, or other APIs.
    """

    @abstractmethod
    def execute_prompt(
        self,
        prompt: str,
        config: RunConfig,
    ) -> LLMResponse:
        """
        Execute a prompt with the LLM.

        Args:
            prompt: Compiled prompt text
            config: Execution configuration

        Returns:
            LLMResponse with generated content

        Raises:
            Exception: On LLM API errors
        """
        pass

    @abstractmethod
    def validate_config(self, config: RunConfig) -> bool:
        """
        Validate that configuration is supported.

        Args:
            config: Configuration to validate

        Returns:
            True if valid, False otherwise
        """
        pass


class MockLLMAdapter(LLMAdapter):
    """
    Mock LLM adapter for testing.

    Returns deterministic responses without calling external APIs.
    """

    def __init__(self, mock_response: str = "Mock LLM response"):
        """
        Initialize mock adapter.

        Args:
            mock_response: Response to return
        """
        self.mock_response = mock_response
        self.call_count = 0
        self.last_prompt = None
        self.last_config = None

    def execute_prompt(
        self,
        prompt: str,
        config: RunConfig,
    ) -> LLMResponse:
        """
        Return mock response.

        Args:
            prompt: Prompt (stored for inspection)
            config: Config (stored for inspection)

        Returns:
            Mock LLMResponse
        """
        self.call_count += 1
        self.last_prompt = prompt
        self.last_config = config

        return LLMResponse(
            content=self.mock_response,
            model=config.model_name,
            usage={
                "prompt_tokens": len(prompt.split()),
                "completion_tokens": len(self.mock_response.split()),
                "total_tokens": len(prompt.split()) + len(self.mock_response.split()),
            },
            finish_reason="stop",
            metadata={"mock": True},
        )

    def validate_config(self, config: RunConfig) -> bool:
        """
        Mock validation always succeeds.

        Args:
            config: Configuration

        Returns:
            Always True
        """
        return True

    def reset(self) -> None:
        """Reset mock state."""
        self.call_count = 0
        self.last_prompt = None
        self.last_config = None


class ErrorLLMAdapter(LLMAdapter):
    """
    Mock adapter that always raises an error.

    Useful for testing error handling.
    """

    def __init__(self, error_message: str = "Mock LLM error"):
        """
        Initialize error adapter.

        Args:
            error_message: Error message to raise
        """
        self.error_message = error_message

    def execute_prompt(
        self,
        prompt: str,
        config: RunConfig,
    ) -> LLMResponse:
        """
        Raise error.

        Args:
            prompt: Prompt
            config: Config

        Raises:
            RuntimeError: Always
        """
        raise RuntimeError(self.error_message)

    def validate_config(self, config: RunConfig) -> bool:
        """
        Validation succeeds.

        Args:
            config: Configuration

        Returns:
            True
        """
        return True
