"""Provider implementation backed by agno models."""

from __future__ import annotations

import inspect
from typing import Any

from asedclaw.providers.base import LLMProvider, LLMResponse


class AgnoOpenAILikeProvider(LLMProvider):
    """LLM provider backed by agno model classes."""

    def __init__(
        self,
        api_key: str | None = None,
        api_base: str | None = None,
        default_model: str = "gpt-4o",
        provider_name: str | None = None,
        extra_headers: dict[str, str] | None = None,
    ):
        super().__init__(api_key=api_key, api_base=api_base)
        self.default_model = default_model
        self.provider_name = provider_name
        self.extra_headers = extra_headers or {}

    @staticmethod
    def _normalize_provider_name(provider_name: str | None) -> str:
        return (provider_name or "").strip().lower().replace("-", "_")

    def _normalize_model_id(self, model: str) -> str:
        provider = self._normalize_provider_name(self.provider_name)
        if not provider:
            return model
        candidates = [f"{provider}/", f"{provider.replace('_', '-')}/"]
        for prefix in candidates:
            if model.startswith(prefix):
                return model[len(prefix) :]
        return model

    @staticmethod
    def _instantiate_model(model_cls: type, kwargs: dict[str, Any]) -> Any:
        sig = inspect.signature(model_cls.__init__)
        accepted = {k for k in sig.parameters.keys() if k != "self"}
        filtered = {k: v for k, v in kwargs.items() if k in accepted and v is not None}
        return model_cls(**filtered)

    @staticmethod
    def _resolve_agno_model_cls(provider_name: str) -> type | None:
        try:
            if provider_name == "openai":
                from agno.models.openai import OpenAIChat

                return OpenAIChat
            if provider_name == "azure_openai":
                from agno.models.azure import AzureOpenAI

                return AzureOpenAI
            if provider_name == "openrouter":
                from agno.models.openrouter import OpenRouter

                return OpenRouter
            if provider_name == "deepseek":
                from agno.models.deepseek import DeepSeek

                return DeepSeek
            if provider_name == "dashscope":
                from agno.models.dashscope import DashScope

                return DashScope
            if provider_name == "groq":
                from agno.models.groq import Groq

                return Groq
            if provider_name == "gemini":
                from agno.models.google import Gemini

                return Gemini
            if provider_name == "moonshot":
                from agno.models.moonshot import MoonShot

                return MoonShot
            if provider_name == "siliconflow":
                from agno.models.siliconflow import Siliconflow

                return Siliconflow
            if provider_name == "vllm":
                from agno.models.vllm import VLLM

                return VLLM
            if provider_name == "anthropic":
                from agno.models.anthropic import Claude

                return Claude
        except Exception:
            return None
        return None

    def build_model(
        self,
        model: str | None = None,
        *,
        temperature: float | None = None,
        max_tokens: int | None = None,
        reasoning_effort: str | None = None,
    ) -> Any:
        from agno.models.openai import OpenAILike

        model_id = self._normalize_model_id(model or self.default_model)
        provider = self._normalize_provider_name(self.provider_name)
        request_params: dict[str, Any] = {}
        if self.extra_headers:
            request_params["extra_headers"] = self.extra_headers
        if max_tokens:
            request_params["max_tokens"] = max(1, int(max_tokens))
        if reasoning_effort:
            request_params["reasoning_effort"] = reasoning_effort

        base_kwargs: dict[str, Any] = {
            "id": model_id,
            "temperature": temperature,
            "api_key": self.api_key,
            "base_url": self.api_base,
            "request_params": request_params or None,
        }
        # Azure endpoints are commonly configured as api_base.
        if provider == "azure_openai":
            base_kwargs["azure_endpoint"] = self.api_base

        model_cls = self._resolve_agno_model_cls(provider) or OpenAILike
        return self._instantiate_model(model_cls, base_kwargs)

    async def chat(
        self,
        messages: list[dict[str, Any]],
        tools: list[dict[str, Any]] | None = None,
        model: str | None = None,
        max_tokens: int = 4096,
        temperature: float = 0.7,
        reasoning_effort: str | None = None,
    ) -> LLMResponse:
        """
        Compatibility chat entrypoint.

        Runtime path has been migrated to agno Agent directly; this method remains
        for compatibility with legacy callers.
        """
        try:
            from agno.agent.agent import Agent
        except Exception as e:
            return LLMResponse(content=f"Error loading agno runtime: {e}", finish_reason="error")

        # Legacy callers pass OpenAI-format messages; we render them into a prompt.
        prompt_parts: list[str] = []
        system_prompt = ""
        for m in messages:
            role = m.get("role", "user")
            content = m.get("content")
            text = content if isinstance(content, str) else str(content)
            if role == "system":
                system_prompt = text
            else:
                prompt_parts.append(f"{role}: {text}")
        prompt = "\n\n".join(prompt_parts) if prompt_parts else ""

        # Tool-calling in provider.chat is deprecated in agno runtime path.
        # Keep parameter for signature compatibility.
        _ = tools

        try:
            agent = Agent(
                model=self.build_model(
                    model=model,
                    temperature=temperature,
                    max_tokens=max_tokens,
                    reasoning_effort=reasoning_effort,
                ),
                instructions=system_prompt or None,
                stream=False,
            )
            run = await agent.arun(prompt, stream=False)
            content = getattr(run, "content", None)
            if not isinstance(content, str):
                content = str(content or "")
            return LLMResponse(content=content, finish_reason="stop")
        except Exception as e:
            return LLMResponse(content=f"Error calling agno model: {e}", finish_reason="error")

    def get_default_model(self) -> str:
        return self.default_model
