"""
Embedding Cache Repository - Redis 向量快取
============================================
2026-03-27 ogt: P1 違規修復 - 模組化改造

職責: Playbook 向量快取 CRUD (Redis)
設計: 依賴注入 Redis client，遵循 Repository Pattern

版本: v1.0
建立: 2026-03-27 (台北時區)
建立者: Claude Code (P1 模組化改造)
"""

import json
from datetime import UTC, datetime

import structlog
from redis.asyncio import Redis

logger = structlog.get_logger(__name__)

# Redis Keys
PLAYBOOK_EMBEDDING_PREFIX = "playbook:embedding:"
PLAYBOOK_EMBEDDING_INDEX = "playbook:embedding:index"

# Cache TTL: 30 天
EMBEDDING_TTL_SECONDS = 30 * 24 * 60 * 60


class EmbeddingCacheRepository:
    """
    Embedding Cache Repository 實作

    使用 Redis 儲存 Playbook 向量快取
    """

    def __init__(self, redis: Redis):
        """
        初始化 Repository

        Args:
            redis: Redis async client (DI 注入)
        """
        self._redis = redis

    async def store(
        self,
        playbook_id: str,
        embedding: list[float],
        metadata: dict | None = None,
    ) -> bool:
        """
        儲存 Playbook 向量到 Redis

        存儲格式:
        - playbook:embedding:{id} -> {vector: [...], metadata: {...}}
        - playbook:embedding:index -> Set of playbook_ids
        """
        try:
            key = f"{PLAYBOOK_EMBEDDING_PREFIX}{playbook_id}"

            data = {
                "vector": embedding,
                "metadata": metadata or {},
                "updated_at": datetime.now(UTC).isoformat(),
            }

            await self._redis.set(
                key,
                json.dumps(data),
                ex=EMBEDDING_TTL_SECONDS,
            )

            # 更新索引
            await self._redis.sadd(PLAYBOOK_EMBEDDING_INDEX, playbook_id)

            logger.debug(
                "playbook_embedding_stored",
                playbook_id=playbook_id,
                vector_dim=len(embedding),
            )
            return True

        except Exception as e:
            logger.warning(
                "playbook_embedding_store_failed",
                playbook_id=playbook_id,
                error=str(e),
            )
            return False

    async def get(self, playbook_id: str) -> list[float] | None:
        """取得 Playbook 向量"""
        try:
            key = f"{PLAYBOOK_EMBEDDING_PREFIX}{playbook_id}"

            data = await self._redis.get(key)
            if not data:
                return None

            parsed = json.loads(data)
            return parsed.get("vector")

        except Exception as e:
            logger.warning(
                "playbook_embedding_get_failed",
                playbook_id=playbook_id,
                error=str(e),
            )
            return None

    async def get_all(self) -> dict[str, list[float]]:
        """取得所有 Playbook 向量"""
        try:
            # 取得所有 playbook_id
            playbook_ids = await self._redis.smembers(PLAYBOOK_EMBEDDING_INDEX)
            if not playbook_ids:
                return {}

            # 批次取得向量
            result = {}
            for pid in playbook_ids:
                pid_str = pid.decode() if isinstance(pid, bytes) else pid
                vec = await self.get(pid_str)
                if vec:
                    result[pid_str] = vec

            return result

        except Exception as e:
            logger.warning(
                "playbook_embeddings_get_all_failed",
                error=str(e),
            )
            return {}

    async def remove(self, playbook_id: str) -> bool:
        """移除 Playbook 向量索引"""
        try:
            key = f"{PLAYBOOK_EMBEDDING_PREFIX}{playbook_id}"

            await self._redis.delete(key)
            await self._redis.srem(PLAYBOOK_EMBEDDING_INDEX, playbook_id)

            logger.info("playbook_embedding_removed", playbook_id=playbook_id)
            return True

        except Exception as e:
            logger.warning(
                "playbook_embedding_remove_failed",
                playbook_id=playbook_id,
                error=str(e),
            )
            return False


# =============================================================================
# Factory Function (for DI)
# =============================================================================


def create_embedding_cache_repository(redis: Redis) -> EmbeddingCacheRepository:
    """
    建立 EmbeddingCacheRepository 實例

    Args:
        redis: Redis async client

    Returns:
        EmbeddingCacheRepository 實例
    """
    return EmbeddingCacheRepository(redis)
