import pytest
from asgiref.sync import sync_to_async
from django.conf import settings
from django.utils import timezone

from promptgame.gameui.attack import recompute_shadow_balance_inplace
from promptgame.tests.helpers import async_make_account


@pytest.mark.django_db
@pytest.mark.asyncio
async def test_shadow_balance() -> None:
    account = await async_make_account()
    # New accounts should start with the max shadow balance
    shadow_balance = account.shadow_balance
    assert shadow_balance == settings.SHADOW_BALANCE_CAP
    # Updated just a bit ago
    assert abs((timezone.now() - account.shadow_balance_updated_at).total_seconds()) < 5
    one_half_balance = int(settings.SHADOW_BALANCE_CAP / 2)
    account.shadow_balance = one_half_balance

    # Simulate waiting a bit by manually setting shadow_balance_updated_at
    # We are currently at 1/2 the balance. If we wait for 1/4 of the interval, we should be at 3/4 the balance.
    one_fourth_interval = settings.SHADOW_BALANCE_INTERVAL / 4
    account.shadow_balance_updated_at = timezone.now() - one_fourth_interval
    three_fourth_balance = int(settings.SHADOW_BALANCE_CAP * 3 / 4)
    await sync_to_async(recompute_shadow_balance_inplace)(account)
    updated_shadow_balance = account.shadow_balance
    assert abs(updated_shadow_balance - three_fourth_balance) < 1
    # Check that this update also updates shadow_balance_updated_at
    assert abs((timezone.now() - account.shadow_balance_updated_at).total_seconds()) < 5

    # Test that is we wait for a long, long time, we're still capped at the max
    ten_years_ago = timezone.now() - timezone.timedelta(days=365 * 10)
    account.shadow_balance_updated_at = ten_years_ago
    await sync_to_async(recompute_shadow_balance_inplace)(account)
    assert account.shadow_balance == settings.SHADOW_BALANCE_CAP

    # Test that even if a user messes with the clock and somehow resets the clock, we floor at 0
    account.shadow_balance = 0
    account.shadow_balance_updated_at = timezone.now() + timezone.timedelta(
        days=365 * 10
    )
    await sync_to_async(recompute_shadow_balance_inplace)(account)
    assert abs(account.shadow_balance) < 1
