"""RedisResponseCache 单元测试。"""

from __future__ import annotations

import pytest

from adapters.redis_cache import RedisResponseCache
from core.types import LLMResponse


@pytest.fixture()
def fake_redis():
    """创建 fakeredis 异步实例。"""
    try:
        import fakeredis.aioredis
    except ImportError:
        pytest.skip("fakeredis not installed")
    return fakeredis.aioredis.FakeRedis(decode_responses=True)


@pytest.fixture()
def sample_response() -> LLMResponse:
    """构造一个标准测试用 LLMResponse。"""
    return LLMResponse(
        content="你好世界",
        thinking="思考过程",
        model="gpt-4o",
        provider="openai",
        prompt_tokens=10,
        completion_tokens=5,
        latency_ms=120,
        ttft_ms=45.0,
        max_inter_token_ms=12.3,
        cache_hit=False,
        call_id="test-call-001",
    )


MESSAGES = [{"role": "user", "content": "你好"}]


class _BrokenRedis:
    """模拟 Redis 连接异常的假实现。"""

    async def get(self, _key: str) -> None:
        raise ConnectionError("redis down")

    async def set(self, _key: str, _value: str, **_kw: object) -> None:
        raise ConnectionError("redis down")


# ── 测试用例 ──────────────────────────────────────────────


@pytest.mark.asyncio
async def test_cache_miss_returns_none(fake_redis: object) -> None:
    """缓存未命中时返回 None。"""
    cache = RedisResponseCache(redis=fake_redis, ttl_s=60)
    result = await cache.get("gpt-4o", MESSAGES)
    assert result is None


@pytest.mark.asyncio
async def test_cache_roundtrip(
    fake_redis: object, sample_response: LLMResponse
) -> None:
    """set 后 get 应返回相同内容。"""
    cache = RedisResponseCache(redis=fake_redis, ttl_s=300)
    await cache.set("gpt-4o", MESSAGES, sample_response)
    result = await cache.get("gpt-4o", MESSAGES)

    assert result is not None
    assert result.content == sample_response.content
    assert result.thinking == sample_response.thinking
    assert result.model == sample_response.model
    assert result.provider == sample_response.provider
    assert result.prompt_tokens == sample_response.prompt_tokens
    assert result.completion_tokens == sample_response.completion_tokens
    assert result.latency_ms == sample_response.latency_ms
    assert result.ttft_ms == sample_response.ttft_ms
    assert result.max_inter_token_ms == sample_response.max_inter_token_ms
    assert result.cache_hit == sample_response.cache_hit
    assert result.call_id == sample_response.call_id


@pytest.mark.asyncio
async def test_different_messages_different_keys(
    fake_redis: object, sample_response: LLMResponse
) -> None:
    """不同 messages 应产生不同缓存键。"""
    cache = RedisResponseCache(redis=fake_redis, ttl_s=300)
    messages_a = [{"role": "user", "content": "问题A"}]
    messages_b = [{"role": "user", "content": "问题B"}]

    await cache.set("gpt-4o", messages_a, sample_response)

    assert await cache.get("gpt-4o", messages_a) is not None
    assert await cache.get("gpt-4o", messages_b) is None


@pytest.mark.asyncio
async def test_different_models_different_keys(
    fake_redis: object, sample_response: LLMResponse
) -> None:
    """不同 model 应产生不同缓存键。"""
    cache = RedisResponseCache(redis=fake_redis, ttl_s=300)

    await cache.set("gpt-4o", MESSAGES, sample_response)

    assert await cache.get("gpt-4o", MESSAGES) is not None
    assert await cache.get("claude-3-opus", MESSAGES) is None


@pytest.mark.asyncio
async def test_graceful_degradation_on_error() -> None:
    """Redis 不可用时静默降级：get→None，set→不报错。"""
    cache = RedisResponseCache(redis=_BrokenRedis(), ttl_s=60)
    response = LLMResponse(
        content="x",
        thinking="",
        model="m",
        provider="p",
        prompt_tokens=0,
        completion_tokens=0,
        latency_ms=0,
        ttft_ms=None,
        max_inter_token_ms=None,
        cache_hit=False,
        call_id="broken-test",
    )

    # get 应返回 None 而非抛异常
    assert await cache.get("m", MESSAGES) is None

    # set 应静默吞掉异常
    await cache.set("m", MESSAGES, response)
