"""
Learning Repository - Redis 持久化層
====================================
Phase D-G P0 修正: 符合 leWOOOgo 積木化原則

職責:
- 學習數據 Redis 持久化
- 修復結果記錄
- 統計查詢

版本: v1.0
建立: 2026-03-29 (台北時區)
建立者: Claude Code (Phase D-G P0 修正)

遵循原則:
- Repository 層負責資料存取
- Service 層只透過 Interface 依賴
- 不在 Service 層直接存取 Redis
"""

import json

import structlog

from src.core.redis_client import get_redis
from src.repositories.interfaces import ILearningRepository
from src.utils.timezone import now_taipei

logger = structlog.get_logger(__name__)


class LearningRepository:
    """
    Learning Repository 實作

    Redis Key 結構:
    - learning:repair:{anomaly_key}:{action} -> List[JSON] (歷史記錄)
    - learning:stats:{anomaly_key}:{action} -> Hash (統計)
    """

    # TTL: 90 天
    HISTORY_TTL = 90 * 24 * 3600
    STATS_TTL = 90 * 24 * 3600

    def __init__(self, redis_client=None):
        """
        初始化 Repository

        Args:
            redis_client: Redis 客戶端 (預設使用共用實例)
        """
        self._redis = redis_client

    def _get_redis(self):
        """Lazy initialization for Redis client"""
        if self._redis is None:
            self._redis = get_redis()
        return self._redis

    # =========================================================================
    # ILearningRepository Implementation
    # =========================================================================

    async def record_repair(
        self,
        anomaly_key: str,
        repair_action: str,
        success: bool,
        root_cause: str | None = None,
        fix_description: str | None = None,
        execution_time_seconds: float | None = None,
    ) -> bool:
        """
        記錄修復結果

        Args:
            anomaly_key: 異常 key
            repair_action: 修復動作
            success: 是否成功
            root_cause: 根因 (如果找到)
            fix_description: 修復說明
            execution_time_seconds: 執行時間

        Returns:
            bool: 是否成功記錄
        """
        redis = self._get_redis()
        history_key = f"learning:repair:{anomaly_key}:{repair_action}"
        stats_key = f"learning:stats:{anomaly_key}:{repair_action}"

        try:
            # 1. 記錄歷史
            record = {
                "success": success,
                "root_cause": root_cause,
                "fix_description": fix_description,
                "execution_time": execution_time_seconds,
                "timestamp": now_taipei().isoformat(),
            }
            await redis.lpush(history_key, json.dumps(record))
            await redis.ltrim(history_key, 0, 99)  # 保留最近 100 次
            await redis.expire(history_key, self.HISTORY_TTL)

            # 2. 更新統計
            await redis.hincrby(stats_key, "total", 1)
            if success:
                await redis.hincrby(stats_key, "success", 1)
            await redis.expire(stats_key, self.STATS_TTL)

            logger.debug(
                "learning_repair_recorded",
                anomaly_key=anomaly_key,
                action=repair_action,
                success=success,
            )
            return True

        except Exception as e:
            logger.error(
                "learning_repair_record_failed",
                anomaly_key=anomaly_key,
                action=repair_action,
                error=str(e),
            )
            return False

    async def get_repair_stats(
        self,
        anomaly_key: str,
        repair_action: str,
    ) -> dict:
        """
        取得修復統計

        Returns:
            {
                "total": int,
                "success": int,
                "success_rate": float
            }
        """
        redis = self._get_redis()
        stats_key = f"learning:stats:{anomaly_key}:{repair_action}"

        try:
            data = await redis.hgetall(stats_key)
            total = int(data.get("total", 0))
            success = int(data.get("success", 0))

            return {
                "total": total,
                "success": success,
                "success_rate": success / total if total > 0 else 0.0,
            }
        except Exception as e:
            logger.warning(
                "learning_stats_fetch_failed",
                anomaly_key=anomaly_key,
                action=repair_action,
                error=str(e),
            )
            return {"total": 0, "success": 0, "success_rate": 0.0}

    async def get_all_repair_stats(
        self,
        anomaly_key: str,
    ) -> dict[str, dict]:
        """
        取得所有修復動作的統計

        Returns:
            {
                "restart_pod": {"total": 5, "success": 4, "success_rate": 0.8},
                "scale_up": {"total": 2, "success": 2, "success_rate": 1.0},
                ...
            }
        """
        redis = self._get_redis()
        pattern = f"learning:stats:{anomaly_key}:*"

        result: dict[str, dict] = {}

        try:
            # 使用 SCAN 避免 KEYS 阻塞
            cursor = 0
            while True:
                cursor, keys = await redis.scan(cursor, match=pattern, count=100)
                for key in keys:
                    # 提取 action 名稱
                    action = key.split(":")[-1]
                    data = await redis.hgetall(key)
                    total = int(data.get("total", 0))
                    success = int(data.get("success", 0))
                    result[action] = {
                        "total": total,
                        "success": success,
                        "success_rate": success / total if total > 0 else 0.0,
                    }

                if cursor == 0:
                    break

            return result

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

    async def get_repair_history(
        self,
        anomaly_key: str,
        repair_action: str,
        limit: int = 20,
    ) -> list[dict]:
        """
        取得修復歷史記錄

        Returns:
            list[dict]: 最近的修復記錄 (由新到舊)
        """
        redis = self._get_redis()
        history_key = f"learning:repair:{anomaly_key}:{repair_action}"

        try:
            records = await redis.lrange(history_key, 0, limit - 1)
            return [json.loads(r) for r in records]
        except Exception as e:
            logger.warning(
                "learning_history_fetch_failed",
                anomaly_key=anomaly_key,
                action=repair_action,
                error=str(e),
            )
            return []

    async def get_learning_summary(
        self,
        anomaly_key: str,
    ) -> dict:
        """
        取得學習摘要

        Returns:
            {
                "anomaly_key": str,
                "total_repair_attempts": int,
                "overall_success_rate": float,
                "actions_tried": list[str],
                "best_action": {"action": str, "success_rate": float} | None,
                "learning_status": str  # insufficient, learning, sufficient, excellent
            }
        """
        all_stats = await self.get_all_repair_stats(anomaly_key)

        if not all_stats:
            return {
                "anomaly_key": anomaly_key,
                "total_repair_attempts": 0,
                "overall_success_rate": 0.0,
                "actions_tried": [],
                "best_action": None,
                "learning_status": "insufficient",
            }

        total_attempts = sum(s["total"] for s in all_stats.values())
        total_success = sum(s["success"] for s in all_stats.values())
        overall_rate = total_success / total_attempts if total_attempts > 0 else 0.0

        # 找出最佳動作
        best_action = None
        best_rate = 0.0
        for action, stats in all_stats.items():
            if stats["total"] >= 3 and stats["success_rate"] > best_rate:
                best_rate = stats["success_rate"]
                best_action = {"action": action, "success_rate": best_rate}

        # 判斷學習狀態
        if total_attempts < 3:
            status = "insufficient"
        elif total_attempts < 10:
            status = "learning"
        elif overall_rate >= 0.8:
            status = "excellent"
        else:
            status = "sufficient"

        return {
            "anomaly_key": anomaly_key,
            "total_repair_attempts": total_attempts,
            "overall_success_rate": overall_rate,
            "actions_tried": list(all_stats.keys()),
            "best_action": best_action,
            "learning_status": status,
        }


# =============================================================================
# Singleton
# =============================================================================

_repository: LearningRepository | None = None


def get_learning_repository() -> ILearningRepository:
    """取得 LearningRepository 單例"""
    global _repository
    if _repository is None:
        _repository = LearningRepository()
    return _repository
