"""Base adapter interface for LLM backends."""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Callable


@dataclass
class AdapterResponse:
    """Response from a model adapter call."""

    content: str
    token_usage: int
    duration_seconds: float
    raw_response: dict | None = None


class ModelAdapter(ABC):
    """Abstract base class for model adapters."""

    name: str

    @abstractmethod
    async def call(
        self,
        prompt: str,
        *,
        system: str | None = None,
        max_tokens: int | None = None,
        temperature: float = 0.7,
        working_dir: str | None = None,
    ) -> AdapterResponse:
        """Make a single completion call.

        Args:
            prompt: The user prompt to send.
            system: Optional system prompt.
            max_tokens: Maximum tokens in response.
            temperature: Sampling temperature.
            working_dir: Optional working directory for context.

        Returns:
            AdapterResponse with the model's response.
        """
        ...

    @abstractmethod
    async def run_agentic(
        self,
        task: str,
        *,
        working_dir: str,
        timeout_seconds: int = 1200,
        on_question: Callable[[str], str] | None = None,
    ) -> AdapterResponse:
        """Run an agentic task that may take many minutes and may ask questions.

        Args:
            task: The task description to execute.
            working_dir: Working directory for the agent.
            timeout_seconds: Maximum time for execution.
            on_question: Optional callback for handling questions.

        Returns:
            AdapterResponse with execution results.
        """
        ...

    def estimate_tokens(self, text: str) -> int:
        """Estimate token count for a text string.

        Uses a rough heuristic of ~4 characters per token.

        Args:
            text: The text to estimate tokens for.

        Returns:
            Estimated token count.
        """
        return len(text) // 4

    def estimate_cost(self, tokens: int) -> float:
        """Estimate cost in USD for a number of tokens.

        Uses a rough default of $0.20 per 1000 tokens.

        Args:
            tokens: Number of tokens.

        Returns:
            Estimated cost in USD.
        """
        return tokens / 5000
