"""考试缓存（exam_id → {class_id, subject_id} 映射，内存 LRU）.

对齐 02-architecture-design.md §8.3 ExamCache：
  - 内存 LRU dict，max 10000 条
  - CDC core_edu_exams 事件触发更新
  - CDC core_edu_grades 事件查询获取 class_id（避免 join 查询）

P6 演进：改为 Redis 实现（key: data_ana:exam:{exam_id}，TTL 30 天），
支持多实例共享（对齐 workline §3.5 任务 6.2）.
"""

from collections import OrderedDict
from typing import Any

import structlog

logger = structlog.get_logger(__name__)

# LRU 最大容量
_MAX_SIZE = 10_000


class ExamCache:
    """考试缓存（LRU，max 10000 条）.

    内存实现：OrderedDict，访问/写入时移到末尾（最近使用），
    超容量时弹出头部（最久未使用）.

    线程安全：asyncio 单线程模型下无需加锁.
    """

    def __init__(self, max_size: int = _MAX_SIZE) -> None:
        self._data: OrderedDict[str, dict[str, str]] = OrderedDict()
        self._max_size = max_size

    def upsert(
        self,
        exam_id: str,
        class_id: str = "",
        subject_id: str = "",
        title: str = "",
    ) -> None:
        """更新或插入考试缓存."""
        if not exam_id:
            return

        value: dict[str, str] = {
            "class_id": class_id,
            "subject_id": subject_id,
            "title": title,
        }

        # 已存在则移到末尾（标记为最近使用）
        if exam_id in self._data:
            self._data.move_to_end(exam_id)
        self._data[exam_id] = value

        # LRU 淘汰
        while len(self._data) > self._max_size:
            evicted_key, _ = self._data.popitem(last=False)
            logger.debug("exam_cache_lru_evicted", exam_id=evicted_key)

    def get(self, exam_id: str) -> dict[str, str] | None:
        """查询考试缓存（命中时移到末尾，标记为最近使用）."""
        if not exam_id:
            return None
        value = self._data.get(exam_id)
        if value is not None:
            self._data.move_to_end(exam_id)
        return value

    def get_class_id(self, exam_id: str) -> str:
        """便捷方法：获取 class_id（未命中返回空字符串）."""
        entry = self.get(exam_id)
        return entry.get("class_id", "") if entry else ""

    def get_subject_id(self, exam_id: str) -> str:
        """便捷方法：获取 subject_id（未命中返回空字符串）."""
        entry = self.get(exam_id)
        return entry.get("subject_id", "") if entry else ""

    def delete(self, exam_id: str) -> bool:
        """删除缓存项."""
        if exam_id in self._data:
            del self._data[exam_id]
            return True
        return False

    def clear(self) -> None:
        """清空缓存."""
        self._data.clear()

    def size(self) -> int:
        """当前缓存数量."""
        return len(self._data)

    def stats(self) -> dict[str, Any]:
        """缓存统计信息（供 /readyz 和监控使用）."""
        return {
            "size": len(self._data),
            "max_size": self._max_size,
            "utilization": round(len(self._data) / self._max_size, 4),
        }


# 全局缓存（进程级单例）
_exam_cache = ExamCache()


def get_exam_cache() -> ExamCache:
    """获取全局 ExamCache 实例."""
    return _exam_cache
