"""
Pytest fixtures for lewooogo-data tests
========================================

提供測試所需的 Mock 與 Fixtures
"""

import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from pydantic import BaseModel


# =============================================================================
# 測試用 Pydantic Model
# =============================================================================

class SampleModel(BaseModel):
    """測試用的 Pydantic Model"""
    id: str
    name: str
    value: int = 0


# =============================================================================
# Redis Mock Fixtures
# =============================================================================

@pytest.fixture
def mock_redis():
    """
    Mock Redis client

    模擬 redis.asyncio 的行為
    """
    mock = AsyncMock()

    # 內部儲存
    storage = {}
    ttls = {}

    async def mock_get(key):
        return storage.get(key)

    async def mock_setex(key, ttl, value):
        storage[key] = value
        ttls[key] = ttl
        return True

    async def mock_delete(key):
        if key in storage:
            del storage[key]
            if key in ttls:
                del ttls[key]
            return 1
        return 0

    async def mock_exists(key):
        return 1 if key in storage else 0

    async def mock_ttl(key):
        if key not in storage:
            return -2
        return ttls.get(key, -1)

    async def mock_expire(key, ttl):
        if key in storage:
            ttls[key] = ttl
            return True
        return False

    async def mock_ping():
        return True

    mock.get = mock_get
    mock.setex = mock_setex
    mock.delete = mock_delete
    mock.exists = mock_exists
    mock.ttl = mock_ttl
    mock.expire = mock_expire
    mock.ping = mock_ping
    mock.close = AsyncMock()

    # 暴露 storage 供測試驗證
    mock._storage = storage
    mock._ttls = ttls

    return mock


@pytest.fixture
def patch_redis_pool(mock_redis):
    """
    Patch Redis 連線池

    將全域 _redis_pool 替換為 mock
    """
    with patch(
        "lewooogo_data.providers.redis_memory._redis_pool",
        mock_redis
    ):
        with patch(
            "lewooogo_data.providers.redis_memory.get_redis_pool",
            return_value=mock_redis
        ):
            yield mock_redis


# =============================================================================
# PostgreSQL Mock Fixtures
# =============================================================================

@pytest.fixture
def mock_session():
    """
    Mock SQLAlchemy AsyncSession
    """
    session = AsyncMock()

    # 內部儲存
    storage = {}

    async def mock_get(model_class, key):
        return storage.get(key)

    session.get = mock_get
    session.add = MagicMock()
    session.delete = AsyncMock()
    session.commit = AsyncMock()

    # 暴露 storage 供測試驗證
    session._storage = storage

    return session


@pytest.fixture
def mock_session_factory(mock_session):
    """
    Mock Session Factory

    使用 context manager 模式
    """
    class MockSessionFactory:
        def __init__(self, session):
            self._session = session

        def __call__(self):
            return self

        async def __aenter__(self):
            return self._session

        async def __aexit__(self, *args):
            pass

    return MockSessionFactory(mock_session)


@pytest.fixture
def patch_pg_session(mock_session_factory):
    """
    Patch PostgreSQL session factory
    """
    with patch(
        "lewooogo_data.providers.pg_memory._session_factory",
        mock_session_factory
    ):
        with patch(
            "lewooogo_data.providers.pg_memory.get_session_factory",
            return_value=mock_session_factory
        ):
            yield mock_session_factory
