from __future__ import annotations

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


class BaseExternalLLM(ABC):
    """
    Base adapter for user-supplied LLM backends.

    Subclass this and implement `invoke(...)`.
    Navexa can call instances directly via `llm_callable=your_adapter`.
    """

    def __init__(self, *, provider: str = "custom", default_model: Optional[str] = None) -> None:
        self.provider = str(provider or "custom").strip().lower()
        self.default_model = default_model

    def resolve_model(self, model: Optional[str]) -> str:
        resolved = (model or self.default_model or "").strip()
        if not resolved:
            raise ValueError(
                "Model is required for external LLM adapter. "
                "Pass model=... in Navexa call or set default_model on adapter."
            )
        return resolved

    @abstractmethod
    def invoke(self, *, prompt: str, model: str, stage: str) -> Any:
        """
        Implement provider call here and return either:
        - string
        - dict/list (Navexa serializes to JSON)
        """
        raise NotImplementedError

    def __call__(self, prompt: str, model: Optional[str], stage: str) -> Any:
        return self.invoke(prompt=prompt, model=self.resolve_model(model), stage=stage)
