from __future__ import annotations

import json
from dataclasses import dataclass, field
from typing import Any, Awaitable, Callable, Protocol


JsonDict = dict[str, Any]


@dataclass(slots=True)
class ToolCall:
    id: str
    name: str
    arguments: JsonDict = field(default_factory=dict)


@dataclass(slots=True)
class ProviderUsage:
    input_tokens: int = 0
    output_tokens: int = 0
    total_tokens: int = 0
    raw: JsonDict = field(default_factory=dict)


@dataclass(slots=True)
class ProviderResponse:
    text: str = ""
    tool_calls: list[ToolCall] = field(default_factory=list)
    usage: ProviderUsage = field(default_factory=ProviderUsage)
    finish_reason: str | None = None
    raw: Any = None


@dataclass(slots=True)
class ChatMessage:
    role: str
    content: str
    name: str | None = None
    tool_call_id: str | None = None


@dataclass(slots=True)
class ChatRequest:
    system_prompt: str
    messages: list[ChatMessage]
    tools: list["ToolSpec"] = field(default_factory=list)
    model: str | None = None
    temperature: float | None = None
    provider_name: str | None = None
    metadata: JsonDict = field(default_factory=dict)


@dataclass(slots=True)
class ToolResult:
    ok: bool = True
    content: Any = None
    error: str | None = None
    metadata: JsonDict = field(default_factory=dict)

    def to_prompt_string(self) -> str:
        payload = {
            "ok": self.ok,
            "content": self.content,
            "error": self.error,
            "metadata": self.metadata,
        }
        return json.dumps(payload, ensure_ascii=False)


@dataclass(slots=True)
class ChatContext:
    session_id: str
    user_id: int | None = None
    chat_id: int | None = None
    reply_to_message_id: int | None = None
    raw_event: JsonDict = field(default_factory=dict)


@dataclass(slots=True)
class ToolContext:
    config: Any
    runtime: Any
    session: Any
    chat: ChatContext


ToolHandler = Callable[[ToolContext, JsonDict], Awaitable[ToolResult]]


@dataclass(slots=True)
class ToolSpec:
    name: str
    description: str
    parameters: JsonDict
    handler: ToolHandler
    dangerous: bool = False

    def to_openai_schema(self) -> JsonDict:
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": self.parameters,
            },
        }

    def to_gemini_schema(self) -> JsonDict:
        return {
            "name": self.name,
            "description": self.description,
            "parameters": self.parameters,
        }


class Provider(Protocol):
    name: str

    async def generate(self, request: ChatRequest) -> ProviderResponse:
        raise NotImplementedError

    async def list_models(self) -> list[str]:
        raise NotImplementedError
