"""Beaver provider 子系统的统一契约。"""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any


@dataclass(slots=True)
class ToolCallRequest:
    """模型返回的一次工具调用请求。"""

    id: str
    name: str
    arguments: dict[str, Any]


@dataclass(slots=True)
class LLMResponse:
    """统一的模型响应结构。"""

    content: str | None
    tool_calls: list[ToolCallRequest] = field(default_factory=list)
    finish_reason: str = "stop"
    usage: dict[str, Any] = field(default_factory=dict)
    reasoning_content: str | None = None
    provider_name: str | None = None
    model: str | None = None

    @property
    def has_tool_calls(self) -> bool:
        return bool(self.tool_calls)


class LLMProvider(ABC):
    """所有 provider 实现必须遵守的统一接口。"""

    def __init__(
        self,
        api_key: str | None = None,
        api_base: str | None = None,
        request_timeout_seconds: float | None = None,
    ) -> None:
        self.api_key = api_key
        self.api_base = api_base
        self.request_timeout_seconds = (
            max(1.0, float(request_timeout_seconds))
            if request_timeout_seconds is not None
            else None
        )

    @staticmethod
    def sanitize_empty_content(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
        """清理 provider 普遍不接受的空 content。"""

        result: list[dict[str, Any]] = []
        for message in messages:
            content = message.get("content")
            if isinstance(content, str) and content == "":
                clean = dict(message)
                clean["content"] = None if (message.get("role") == "assistant" and message.get("tool_calls")) else "(empty)"
                result.append(clean)
                continue
            if isinstance(content, list):
                filtered = [
                    item
                    for item in content
                    if not (
                        isinstance(item, dict)
                        and item.get("type") in ("text", "input_text", "output_text")
                        and not item.get("text")
                    )
                ]
                if len(filtered) != len(content):
                    clean = dict(message)
                    clean["content"] = filtered or "(empty)"
                    if message.get("role") == "assistant" and message.get("tool_calls") and not filtered:
                        clean["content"] = None
                    result.append(clean)
                    continue
            result.append(message)
        return result

    @abstractmethod
    async def chat(
        self,
        messages: list[dict[str, Any]],
        tools: list[dict[str, Any]] | None = None,
        model: str | None = None,
        max_tokens: int | None = None,
        temperature: float = 0.7,
        thinking_enabled: bool | None = None,
    ) -> LLMResponse:
        """统一聊天接口。"""

    @abstractmethod
    def get_default_model(self) -> str:
        """返回 provider 的默认模型名。"""
