"""Refresh token Redis + MySQL dual persistence."""

from datetime import datetime, timedelta, timezone
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from server.application.services.auth_service import AuthService
from server.domain.models import Admin


@pytest.mark.asyncio
async def test_membership_mysql_fallback_rehydrates_redis():
    admin = Admin(id=1, username="admin", password_hash="x", is_active=True, token_version=1)
    refresh_repo = AsyncMock()
    refresh_repo.exists_valid = AsyncMock(return_value=True)

    service = AuthService(
        admin_repo=AsyncMock(),
        attempt_repo=AsyncMock(),
        audit_repo=AsyncMock(),
        refresh_token_repo=refresh_repo,
    )
    service._redis_sadd_refresh = AsyncMock(return_value=True)

    with patch("server.infrastructure.redis.client.get_redis") as get_redis:
        redis = AsyncMock()
        redis.sismember = AsyncMock(return_value=False)
        get_redis.return_value = redis

        result = await service._refresh_token_membership(1, "secret:1:1")

    assert result is True
    refresh_repo.exists_valid.assert_awaited_once()
    service._redis_sadd_refresh.assert_awaited_once()


@pytest.mark.asyncio
async def test_store_writes_redis_then_mysql():
    refresh_repo = AsyncMock()
    service = AuthService(
        admin_repo=AsyncMock(),
        attempt_repo=AsyncMock(),
        audit_repo=AsyncMock(),
        refresh_token_repo=refresh_repo,
    )
    service._redis_sadd_refresh = AsyncMock(return_value=True)

    await service._store_refresh_token(2, "tok:2:0")

    service._redis_sadd_refresh.assert_awaited_once()
    refresh_repo.add.assert_awaited_once()
    _admin_id, token_hash, expires_at = refresh_repo.add.await_args.args
    assert _admin_id == 2
    assert len(token_hash) == 64
    assert expires_at > datetime.now(timezone.utc)


@pytest.mark.asyncio
async def test_delete_all_clears_both_layers():
    refresh_repo = AsyncMock()
    service = AuthService(
        admin_repo=AsyncMock(),
        attempt_repo=AsyncMock(),
        audit_repo=AsyncMock(),
        refresh_token_repo=refresh_repo,
    )
    service._redis_delete_all_refresh = AsyncMock()

    await service._delete_all_refresh_tokens(3)

    service._redis_delete_all_refresh.assert_awaited_once_with(3)
    refresh_repo.delete_all_for_admin.assert_awaited_once_with(3)
