from __future__ import annotations

"""Core protocols used across the SDK."""

from abc import ABC, abstractmethod
from typing import AsyncIterator

from .types import (
    AgentOutput,
    CompletionRequest,
    CompletionResponse,
    Message,
    StreamChunk,
    ToolOutput,
)


class LlmProvider(ABC):
    """Unified contract for chat-completion capable LLM providers."""

    @abstractmethod
    async def complete(self, request: CompletionRequest) -> CompletionResponse:
        """Execute a single non-streaming completion request."""

    @abstractmethod
    async def stream(self, request: CompletionRequest) -> AsyncIterator[StreamChunk]:
        """Execute a streaming completion request."""

    @abstractmethod
    def name(self) -> str:
        """Return the provider name."""


class Tool(ABC):
    """Unified contract for agent tools."""

    @abstractmethod
    def name(self) -> str:
        """Return the public tool name."""

    @abstractmethod
    def description(self) -> str:
        """Return a short tool description."""

    @abstractmethod
    def schema(self) -> dict:
        """Return a JSON-schema-like description of the tool input."""

    @abstractmethod
    async def call(self, input_value: object) -> ToolOutput:
        """Execute the tool."""


class Memory(ABC):
    """Contract for agent memory backends."""

    @abstractmethod
    async def store(self, key: str, value: Message) -> None:
        """Store a message under a logical key."""

    @abstractmethod
    async def retrieve(self, query: str, limit: int) -> list[Message]:
        """Retrieve relevant messages for a query."""

    @abstractmethod
    async def history(self) -> list[Message]:
        """Return the full conversation history."""

    @abstractmethod
    async def clear(self) -> None:
        """Clear the memory backend."""


class AgentTrait(ABC):
    """Contract for executable agents."""

    @abstractmethod
    async def run(self, input_text: str) -> AgentOutput:
        """Run the agent to completion for one user input."""

    @abstractmethod
    async def stream_run(self, input_text: str):
        """Run the agent as an event stream."""
