"""MemoryClient — the main entry point for the memory SDK.

Provides write() and retrieve() as the two primary operations.
All dependencies are injected via constructor (LLM, Embeddings, Config).
"""

from __future__ import annotations

from dataclasses import dataclass, field

from arandu.config import MemoryConfig
from arandu.db import create_engine, create_session_factory, init_db
from arandu.protocols import EmbeddingProvider, LLMProvider


@dataclass
class WriteResult:
    """Result of a memory write operation."""

    event_id: str = ""
    facts_added: list = field(default_factory=list)
    facts_updated: list = field(default_factory=list)
    facts_unchanged: int = 0
    facts_deleted: int = 0
    entities_resolved: list = field(default_factory=list)
    duration_ms: float = 0.0


@dataclass
class ScoredFact:
    """A fact with retrieval scores."""

    fact_id: str = ""
    entity_name: str = ""
    attribute_key: str = ""
    value: str = ""
    score: float = 0.0
    scores: dict = field(default_factory=dict)


@dataclass
class RetrieveResult:
    """Result of a memory retrieve operation."""

    facts: list[ScoredFact] = field(default_factory=list)
    context: str = ""
    total_candidates: int = 0
    duration_ms: float = 0.0


class MemoryClient:
    """Main entry point for the arandu SDK.

    Usage::

        from arandu import MemoryClient, MemoryConfig
        from arandu.providers.openai import OpenAIProvider

        provider = OpenAIProvider(api_key="sk-...")
        memory = MemoryClient(
            database_url="postgresql+psycopg://user:pass@localhost/mydb",
            llm=provider,
            embeddings=provider,
        )
        await memory.initialize()

        result = await memory.write(user_id="user_123", message="I live in São Paulo")
        context = await memory.retrieve(user_id="user_123", query="where does the user live?")
    """

    def __init__(
        self,
        database_url: str,
        llm: LLMProvider,
        embeddings: EmbeddingProvider,
        config: MemoryConfig | None = None,
    ) -> None:
        self._config = config or MemoryConfig()
        self._llm = llm
        self._embeddings = embeddings
        self._engine = create_engine(database_url)
        self._session_factory = create_session_factory(self._engine)
        self._initialized = False

    async def initialize(self) -> None:
        """Create memory tables in the database (idempotent)."""
        await init_db(self._engine)
        self._initialized = True

    @property
    def config(self) -> MemoryConfig:
        return self._config

    async def write(self, user_id: int | str, message: str, source: str = "api") -> WriteResult:
        """Extract facts from a message and store them in memory.

        Args:
            user_id: Unique identifier for the user.
            message: The user's message text.
            source: Source channel identifier (default: "api").

        Returns:
            WriteResult with details of extracted and stored facts.
        """
        from arandu.write.pipeline import run_write_pipeline

        async with self._session_factory() as session:
            result = await run_write_pipeline(
                session=session,
                user_id=str(user_id),
                message=message,
                llm=self._llm,
                embeddings=self._embeddings,
                config=self._config,
                source=source,
            )
            await session.commit()

        return WriteResult(
            event_id=result["event_id"],
            facts_added=result["facts_added"],
            facts_updated=result["facts_updated"],
            facts_unchanged=result["facts_unchanged"],
            facts_deleted=result["facts_deleted"],
            entities_resolved=result["entities_resolved"],
            duration_ms=result["duration_ms"],
        )

    async def retrieve(self, user_id: int | str, query: str) -> RetrieveResult:
        """Retrieve relevant memory context for a query.

        Args:
            user_id: Unique identifier for the user.
            query: The query to search memory for.

        Returns:
            RetrieveResult with ranked facts and formatted context string.
        """
        from arandu.read.pipeline import run_read_pipeline

        async with self._session_factory() as session:
            result = await run_read_pipeline(
                session=session,
                user_id=str(user_id),
                query=query,
                llm=self._llm,
                embeddings=self._embeddings,
                config=self._config,
            )

        return RetrieveResult(
            facts=[
                ScoredFact(
                    fact_id=f.fact_id,
                    entity_name=f.entity_name,
                    attribute_key=f.attribute_key,
                    value=f.value,
                    score=f.score,
                    scores=f.scores,
                )
                for f in result.facts
            ],
            context=result.context,
            total_candidates=result.total_candidates,
            duration_ms=result.duration_ms,
        )

    async def close(self) -> None:
        """Dispose of the database engine and release connections."""
        await self._engine.dispose()
