"""Memory API use case: remember, recall, forget.

Agent delegates to functions here. Public API stays on Agent.
"""

from __future__ import annotations

import uuid
from typing import Any, cast

from syrin.enums import Hook, MemoryType
from syrin.events import EventContext
from syrin.memory.config import MemoryEntry
from syrin.observability import SemanticAttributes, SpanKind


def remember(
    agent: Any,
    content: str,
    memory_type: MemoryType = MemoryType.EPISODIC,
    importance: float = 1.0,
    **metadata: Any,
) -> str:
    """Store a fact in persistent memory. Returns memory ID."""
    if agent._memory_backend is None:
        raise RuntimeError("No persistent memory configured")

    tracer = agent._tracer
    with tracer.span(
        "memory.store",
        kind=SpanKind.MEMORY,
        attributes={
            SemanticAttributes.MEMORY_OPERATION: "store",
            SemanticAttributes.MEMORY_TYPE: memory_type.value,
        },
    ) as mem_span:
        entry = MemoryEntry(
            id=str(uuid.uuid4()),
            content=content,
            type=memory_type,
            importance=importance,
            metadata=metadata,
        )
        agent._memory_backend.add(entry)
        mem_span.set_attribute("memory.id", entry.id)
        agent._run_report.memory.stores += 1
        agent._emit_event(
            Hook.MEMORY_STORE,
            EventContext(
                memory_id=entry.id,
                content=content[:100],
                memory_type=memory_type.value,
                importance=importance,
            ),
        )
        return entry.id


def recall(
    agent: Any,
    query: str | None = None,
    memory_type: MemoryType | None = None,
    limit: int = 10,
) -> list[MemoryEntry]:
    """Retrieve memories by query or type."""
    if agent._memory_backend is None:
        raise RuntimeError("No persistent memory configured")

    tracer = agent._tracer
    with tracer.span(
        "memory.recall",
        kind=SpanKind.MEMORY,
        attributes={
            SemanticAttributes.MEMORY_OPERATION: "recall",
            SemanticAttributes.MEMORY_TYPE: memory_type.value if memory_type else "all",
            SemanticAttributes.MEMORY_QUERY: query or "",
        },
    ) as mem_span:
        if query:
            results = agent._memory_backend.search(query, memory_type, limit)
        else:
            results = agent._memory_backend.list(memory_type, limit=limit)

        mem_span.set_attribute(SemanticAttributes.MEMORY_RESULTS_COUNT, len(results))
        agent._run_report.memory.recalls += 1
        agent._emit_event(
            Hook.MEMORY_RECALL,
            EventContext(
                query=query,
                memory_type=memory_type.value if memory_type else "all",
                results_count=len(results),
                limit=limit,
            ),
        )
        return cast(list[MemoryEntry], results)


def forget(
    agent: Any,
    memory_id: str | None = None,
    query: str | None = None,
    memory_type: MemoryType | None = None,
) -> int:
    """Delete one or more memories. Returns count deleted."""
    if agent._memory_backend is None:
        raise RuntimeError("No persistent memory configured")

    tracer = agent._tracer
    with tracer.span(
        "memory.forget",
        kind=SpanKind.MEMORY,
        attributes={
            SemanticAttributes.MEMORY_OPERATION: "forget",
            SemanticAttributes.MEMORY_TYPE: memory_type.value if memory_type else "all",
            SemanticAttributes.MEMORY_QUERY: query or "",
        },
    ) as mem_span:
        deleted = 0
        if memory_id:
            agent._memory_backend.delete(memory_id)
            deleted = 1
        elif query or memory_type:
            memories = agent._memory_backend.list(memory_type)
            for mem in memories:
                if query is None or (query and query.lower() in mem.content.lower()):
                    agent._memory_backend.delete(mem.id)
                    deleted += 1

        mem_span.set_attribute("memory.deleted_count", deleted)
        agent._run_report.memory.forgets += deleted
        agent._emit_event(
            Hook.MEMORY_FORGET,
            EventContext(
                memory_id=memory_id,
                query=query,
                memory_type=memory_type.value if memory_type else "all",
                deleted_count=deleted,
            ),
        )
        return deleted
