"""Semantic Memory - 知识库适配器

适配器模式，对接外部 RAG 服务和知识图谱。
"""

import logging
from typing import Any

from agentkit.memory.base import Memory, MemoryItem

logger = logging.getLogger(__name__)


class SemanticMemory(Memory):
    """Semantic Memory - 知识库检索

    通过适配器对接外部 RAG 服务，不直接依赖具体实现。
    """

    def __init__(
        self,
        rag_service: Any = None,
        graph_service: Any = None,
        knowledge_base_ids: list[str] | None = None,
        search_mode: str = "standard",
        use_rerank: bool = True,
        use_compression: bool = False,
        kb_weights: dict[str, float] | None = None,
    ):
        """
        Args:
            rag_service: RAG 检索服务（需提供 search 方法）
            graph_service: 知识图谱服务（需提供 query 方法）
            knowledge_base_ids: 默认检索的知识库 ID 列表
            search_mode: 检索模式，"standard" 或 "enhanced"
            use_rerank: 启用 rerank 重排序（仅 enhanced 模式生效）
            use_compression: 启用上下文压缩（仅 enhanced 模式生效）
            kb_weights: 知识库权重映射，key 为知识库 ID，value 为权重倍数
        """
        self._rag_service = rag_service
        self._graph_service = graph_service
        self._knowledge_base_ids = knowledge_base_ids or []
        self._search_mode = search_mode
        self._use_rerank = use_rerank
        self._use_compression = use_compression
        self._kb_weights = kb_weights

    async def store(self, key: str, value: Any, metadata: dict[str, Any] | None = None) -> None:
        """Semantic Memory 通常只读，写入委托给 RAG 服务的 ingest 方法"""
        if self._rag_service and hasattr(self._rag_service, 'ingest'):
            await self._rag_service.ingest(key, value, metadata)
        else:
            logger.warning("SemanticMemory.store: no RAG service configured for writing")

    async def retrieve(self, key: str) -> MemoryItem | None:
        """按 key 精确检索（Semantic Memory 通常不按 key 检索）"""
        return None

    async def search(self, query: str, top_k: int = 5, filters: dict[str, Any] | None = None) -> list[MemoryItem]:
        """语义检索知识库"""
        items = []

        # RAG 检索
        if self._rag_service:
            try:
                kb_ids = (filters or {}).get("knowledge_base_ids", self._knowledge_base_ids)
                if self._search_mode == "enhanced" and hasattr(self._rag_service, "enhanced_search"):
                    results = await self._rag_service.enhanced_search(
                        query,
                        knowledge_base_ids=kb_ids,
                        top_k=top_k,
                        use_rerank=self._use_rerank,
                        use_compression=self._use_compression,
                    )
                else:
                    results = await self._rag_service.search(query, knowledge_base_ids=kb_ids, top_k=top_k)
                for r in results:
                    kb_id = r.get("knowledge_base_id", "")
                    score = r.get("score", 0.0)
                    # Apply per-KB weights
                    if self._kb_weights and kb_id in self._kb_weights:
                        score *= self._kb_weights[kb_id]
                    items.append(MemoryItem(
                        key=r.get("id", ""),
                        value=r.get("content", ""),
                        metadata={
                            "source": r.get("source", "rag"),
                            "score": score,
                            "document_id": r.get("document_id"),
                            "knowledge_base_id": kb_id,
                        },
                        score=score,
                    ))
            except Exception as e:
                logger.error(f"RAG search failed: {e}")

        # 知识图谱检索
        if self._graph_service:
            try:
                graph_results = await self._graph_service.query(query, depth=2)
                for r in graph_results[:top_k]:
                    items.append(MemoryItem(
                        key=r.get("id", ""),
                        value=r.get("content", ""),
                        metadata={
                            "source": "graph",
                            "entities": r.get("entities", []),
                            "relations": r.get("relations", []),
                        },
                        score=r.get("score", 0.0),
                    ))
            except Exception as e:
                logger.error(f"Graph search failed: {e}")

        items.sort(key=lambda x: x.score, reverse=True)
        return items[:top_k]

    async def delete(self, key: str) -> bool:
        """Semantic Memory 通常只读"""
        logger.warning("SemanticMemory.delete: read-only memory")
        return False
