import pytest
from asgiref.sync import sync_to_async
from django.contrib.auth.models import User

from promptgame.gameui.models import Account
from promptgame.gameui.user_account_tools import (
    async_create_user_and_account,
    async_get_account,
    create_user_and_account,
    get_account,
)


def make_user() -> User:
    # Create a user without an account
    return User.objects.create(username="testUser")


async_make_user = sync_to_async(make_user)


@pytest.mark.django_db
def test_get_account_sync() -> None:
    # Attempt to get the account using the synchronous method
    user = make_user()
    # confirm the user has no account
    with pytest.raises(Account.DoesNotExist):
        user.account
    account = get_account(user)
    user.refresh_from_db()
    # confirm the user now has an associated account
    assert user.account == account
    assert account.owner == user

    # If a user with an existing account uses the function,
    # the existing account should be returned
    user, account = create_user_and_account()
    assert user.account == account
    assert account.owner == user
    new_account = get_account(user)
    assert new_account == account


@pytest.mark.django_db
@pytest.mark.asyncio
async def test_get_account_async() -> None:
    # Exactly the same as the synchronous test, but using the asynchronous method
    user = await async_make_user()
    # confirm the user has no account
    with pytest.raises(Account.DoesNotExist):
        await Account.objects.aget(owner=user)
    account = await async_get_account(user)
    # confirm the user now has an associated account
    assert (await User.objects.aget(account=account)) == user
    assert (await Account.objects.aget(owner=user)) == account

    # If a user with an existing account uses the function,
    # the existing account should be returned
    user, account = await async_create_user_and_account()
    assert (await User.objects.aget(account=account)) == user
    assert (await Account.objects.aget(owner=user)) == account
    new_account = await async_get_account(user)
    assert new_account == account
