"""
Dual Memory Provider - 雙層記憶體
=================================
Phase 6.4d: 雙層 Memory 實作

統帥鐵律 2026-03-23:
- Working + Episodic 同步寫入
- 讀取時 Working 優先，Episodic 備援
- 任一層失敗不影響整體流程

架構:
┌─────────────────────────────────────────────┐
│           DualMemoryProvider                │
├─────────────────────────────────────────────┤
│  ┌─────────────┐    ┌─────────────────┐    │
│  │   Redis     │ ←→ │   PostgreSQL    │    │
│  │  (Working)  │    │   (Episodic)    │    │
│  │   7天 TTL   │    │     永久        │    │
│  └─────────────┘    └─────────────────┘    │
└─────────────────────────────────────────────┘

讀取策略: Working → Episodic (Cache-Aside)
寫入策略: Working + Episodic 同步
"""

from typing import TypeVar, Type, Generic

import structlog
from pydantic import BaseModel

from ..interfaces.memory_provider import IMemoryProvider, IDualMemoryProvider
from .redis_memory import RedisMemoryProvider
from .pg_memory import PgMemoryProvider

logger = structlog.get_logger(__name__)

T = TypeVar("T", bound=BaseModel)


class DualMemoryProvider(IDualMemoryProvider[T]):
    """
    雙層記憶體提供者

    統帥鐵律:
    - 讀取: Working 優先 → Episodic 備援 → 回填 Working
    - 寫入: Working + Episodic 同步 (任一失敗繼續)
    - 刪除: 兩層同時刪除

    特性:
    - Cache-Aside 模式
    - 優雅降級 (Redis 掛掉不影響 PG)
    - 自動回填 (從 PG 讀取後回填 Redis)
    """

    def __init__(
        self,
        model_class: Type[T],
        key_prefix: str = "awoooi",
        working_ttl: int = 604800,  # 7 天
    ):
        """
        Args:
            model_class: Pydantic Model 類別
            key_prefix: Key 前綴
            working_ttl: Working Memory TTL (秒)
        """
        self._model_class = model_class
        self._working_ttl = working_ttl

        # 初始化兩層 Provider
        self._working = RedisMemoryProvider(
            model_class=model_class,
            key_prefix=key_prefix,
            default_ttl=working_ttl,
        )

        self._episodic = PgMemoryProvider(model_class=model_class)

    @property
    def working(self) -> IMemoryProvider[T]:
        """Working Memory Provider (Redis)"""
        return self._working

    @property
    def episodic(self) -> IMemoryProvider[T]:
        """Episodic Memory Provider (PostgreSQL)"""
        return self._episodic

    async def load(self, key: str) -> T | None:
        """
        載入資料 (Cache-Aside 模式)

        策略:
        1. 先從 Working Memory (Redis) 讀取
        2. 若 miss，從 Episodic Memory (PG) 讀取
        3. 若 PG 有資料，回填到 Redis

        Args:
            key: 資料鍵值

        Returns:
            T | None: 資料物件或 None
        """
        # 1. 嘗試從 Working Memory 讀取
        data = await self._working.load(key)
        if data is not None:
            logger.debug("dual_load_hit_working", key=key)
            return data

        # 2. Working miss，嘗試從 Episodic 讀取
        data = await self._episodic.load(key)
        if data is not None:
            logger.debug("dual_load_hit_episodic", key=key)

            # 3. 回填到 Working Memory
            await self._working.save(key, data, self._working_ttl)
            logger.debug("dual_load_backfill_working", key=key)

            return data

        # 4. 兩層都沒有
        logger.debug("dual_load_miss", key=key)
        return None

    async def save(self, key: str, data: T) -> bool:
        """
        雙層儲存

        策略:
        - Working + Episodic 同步寫入
        - 任一層失敗繼續執行
        - 至少一層成功即回報成功

        Args:
            key: 資料鍵值
            data: 資料物件

        Returns:
            bool: 至少一層成功
        """
        working_ok = False
        episodic_ok = False

        # 1. 寫入 Working Memory (Redis)
        try:
            working_ok = await self._working.save(key, data, self._working_ttl)
        except Exception as e:
            logger.warning("dual_save_working_failed", key=key, error=str(e))

        # 2. 寫入 Episodic Memory (PostgreSQL)
        try:
            episodic_ok = await self._episodic.save(key, data)
        except Exception as e:
            logger.warning("dual_save_episodic_failed", key=key, error=str(e))

        success = working_ok or episodic_ok

        logger.info(
            "dual_saved",
            key=key,
            working=working_ok,
            episodic=episodic_ok,
            success=success,
        )

        return success

    async def delete(self, key: str) -> bool:
        """
        雙層刪除

        Args:
            key: 資料鍵值

        Returns:
            bool: 至少一層成功
        """
        working_ok = await self._working.delete(key)
        episodic_ok = await self._episodic.delete(key)

        success = working_ok or episodic_ok

        logger.info(
            "dual_deleted",
            key=key,
            working=working_ok,
            episodic=episodic_ok,
        )

        return success

    async def exists(self, key: str) -> bool:
        """
        檢查是否存在 (任一層有即為存在)

        Args:
            key: 資料鍵值

        Returns:
            bool: 是否存在
        """
        return await self._working.exists(key) or await self._episodic.exists(key)

    async def sync_to_working(self, key: str) -> bool:
        """
        從 Episodic 同步到 Working (手動回填)

        Args:
            key: 資料鍵值

        Returns:
            bool: 是否成功
        """
        data = await self._episodic.load(key)
        if data is None:
            return False

        return await self._working.save(key, data, self._working_ttl)

    async def sync_to_episodic(self, key: str) -> bool:
        """
        從 Working 同步到 Episodic (手動持久化)

        Args:
            key: 資料鍵值

        Returns:
            bool: 是否成功
        """
        data = await self._working.load(key)
        if data is None:
            return False

        return await self._episodic.save(key, data)
