from __future__ import annotations

from datetime import date, datetime
from typing import Any, Protocol, runtime_checkable

from .models import LLMResponse, Message, SummaryRecord
from .types import PeriodType


@runtime_checkable
class MemoryStore(Protocol):
    """Storage backend for temporal memory.

    Implementations are **pre-scoped** -- they're constructed already bound
    to whatever entity owns the memory (agent, user, session, etc.).
    The temporal package never sees or cares about that scoping.
    """

    def store_messages(self, messages: list[Message]) -> None: ...

    def query_messages(
        self,
        *,
        after: datetime | None = None,
        before: datetime | None = None,
        limit: int | None = None,
    ) -> list[Message]: ...

    def get_raw_message_data(
        self,
        *,
        after: datetime | None = None,
        before: datetime | None = None,
    ) -> list[tuple[datetime, Any]]: ...

    def has_summaries(self, period: PeriodType) -> bool: ...

    def get_summary_keys(self, period: PeriodType) -> set[tuple]: ...

    def query_summaries(
        self,
        period: PeriodType,
        *,
        start: datetime | None = None,
        end: datetime | None = None,
        limit: int | None = None,
    ) -> list[SummaryRecord]: ...

    def get_all_summaries(self, period: PeriodType) -> list[SummaryRecord]: ...

    def get_summaries_before(
        self,
        period: PeriodType,
        before: date,
    ) -> list[SummaryRecord]: ...

    def upsert_summary(
        self,
        period: PeriodType,
        key: dict[str, Any],
        summary: str,
        message_count: int,
    ) -> bool: ...


@runtime_checkable
class LLMClient(Protocol):
    """Async LLM client for summarization and associative memory."""

    async def complete(
        self,
        prompt: str,
        model: str,
        max_tokens: int = 4096,
    ) -> LLMResponse: ...


@runtime_checkable
class MessageFormatter(Protocol):
    """Formats messages for inclusion in memory context or summarization."""

    def format_message(self, message: Message, filter_level: int = 0) -> str | None:
        """Format a single message for context rendering.

        Returns ``None`` to skip the message entirely (e.g. reasoning blocks).
        ``filter_level`` controls compression aggressiveness (0 = least, 4 = most).
        """
        ...

    def serialize_for_summary(self, message: Message) -> dict[str, Any]:
        """Serialize a message for the summarization LLM prompt."""
        ...
