"""Protocol-based storage interfaces for kokoro_chat.

All protocols use ``typing.Protocol`` with ``@runtime_checkable`` so that
concrete implementations can be verified at runtime (e.g. in tests or DI
containers) without requiring inheritance.

No sqlalchemy / fastapi / redis imports allowed in this file.
"""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Protocol, runtime_checkable


# -- Value objects used across protocols ------------------------------------

@dataclass
class ChatMessage:
    """A single chat message returned by ChatHistoryStore."""

    role: str  # "user" | "assistant"
    content: str


@dataclass
class AttitudeRecord:
    """Snapshot of a character's attitude toward a user."""

    affection: int = 50
    trust: int = 50
    mood: str = "neutral"
    modifiers_json: str = "[]"


@dataclass
class MemorySearchResult:
    """A single memory search hit."""

    id: str = ""
    text: str = ""
    relevance: float = 0.0
    metadata: dict[str, Any] = field(default_factory=dict)


# -- Protocols --------------------------------------------------------------

@runtime_checkable
class ChatHistoryStore(Protocol):
    """Read recent chat messages for a user/character pair."""

    async def get_recent(
        self,
        user_id: str,
        character_id: str,
        limit: int = 10,
    ) -> list[ChatMessage]: ...


@runtime_checkable
class AttitudeStore(Protocol):
    """Read and write attitude state for a user/character pair."""

    async def get(
        self,
        user_id: str,
        character_id: str,
    ) -> AttitudeRecord | None: ...

    async def get_or_create(
        self,
        user_id: str,
        character_id: str,
    ) -> AttitudeRecord: ...

    async def save(
        self,
        user_id: str,
        character_id: str,
        affection: int,
        trust: int,
        mood: str,
        modifiers_json: str = "[]",
    ) -> None: ...


@runtime_checkable
class MemoryVectorStore(Protocol):
    """Semantic search and persistence for conversation memories."""

    async def search(
        self,
        query: str,
        character_id: str,
        user_id: str,
        top_k: int = 5,
        room_id: str | None = None,
    ) -> list[MemorySearchResult]: ...

    async def add(
        self,
        character_id: str,
        user_id: str,
        text: str,
        memory_type: str = "conversation_summary",
        importance: float = 0.5,
        room_id: str | None = None,
        visibility: str = "room",
    ) -> str: ...


@runtime_checkable
class MemoStore(Protocol):
    """Key-value memo storage for user personal information."""

    async def get(
        self,
        user_id: str,
        character_id: str,
        key: str | None = None,
    ) -> list[dict[str, str]]: ...

    async def upsert(
        self,
        user_id: str,
        character_id: str,
        key: str,
        value: str,
    ) -> None: ...


# -- Engine-level protocols (used by ChatEngine) ------------------------------

@dataclass
class RollingSummaryRecord:
    """Rolling summary state for a user/character conversation."""

    summary: str = ""
    turn_count: int = 0


@runtime_checkable
class RollingSummaryStore(Protocol):
    """Read and write rolling conversation summaries."""

    async def get_or_create(
        self,
        user_id: str,
        character_id: str,
    ) -> RollingSummaryRecord: ...

    async def update(
        self,
        user_id: str,
        character_id: str,
        summary: str,
        turn_count: int,
    ) -> None: ...


@dataclass
class EventRecord:
    """A single event tied to a character/user pair."""

    event_type: str = ""
    content: str = ""


@runtime_checkable
class EventStore(Protocol):
    """Read events for a character/user pair."""

    async def get_by_character(
        self,
        character_id: str,
        user_id: str,
    ) -> list[EventRecord]: ...


@runtime_checkable
class UsageLogStore(Protocol):
    """Log LLM usage (tokens + cost) for observability."""

    async def log(
        self,
        user_id: str,
        model: str,
        input_tokens: int,
        output_tokens: int,
        cost_usd: float,
        task_type: str,
        character_id: str | None = None,
    ) -> None: ...


@runtime_checkable
class ChatMessageWriter(Protocol):
    """Write a chat message to persistent storage."""

    async def add_message(
        self,
        user_id: str,
        character_id: str,
        role: str,
        content: str,
        chat_mode: str = "short",
    ) -> None: ...


@runtime_checkable
class CacheStore(Protocol):
    """Cache layer for attitude, messages, and rolling summaries.

    This abstracts the Redis/in-memory cache that ChatEngine relies on
    for fast reads.  All methods are optional-style: returning None
    means cache miss.
    """

    async def get_attitude(
        self, user_id: str, character_id: str,
    ) -> dict[str, Any] | None: ...

    async def set_attitude(
        self, user_id: str, character_id: str, data: dict[str, Any],
    ) -> None: ...

    async def get_recent_messages(
        self, user_id: str, character_id: str, limit: int = 20,
    ) -> list[dict[str, str]] | None: ...

    async def set_recent_messages(
        self, user_id: str, character_id: str, messages: list[dict[str, str]],
    ) -> None: ...

    async def append_message(
        self, user_id: str, character_id: str, role: str, content: str,
    ) -> None: ...

    async def get_rolling_summary(
        self, user_id: str, character_id: str,
    ) -> dict[str, Any] | None: ...

    async def set_rolling_summary(
        self, user_id: str, character_id: str, summary: str, turn_count: int,
    ) -> None: ...
