from datetime import UTC, datetime, timedelta
from uuid import UUID, uuid4

import pytest

from template_project.application.access_token.entity import AccessToken
from template_project.application.access_token.errors import AccessTokenExpiredError
from template_project.application.user.entity import UserId


def test_access_token_factory_creates_valid_token() -> None:
    user_id = UserId(uuid4())
    expires_in = timedelta(days=7)

    token = AccessToken.factory(
        user_id=user_id,
        expires_in=expires_in,
    )

    assert isinstance(token.id, UUID)
    assert token.user_id == user_id
    assert token.revoked is False
    assert token.expires_in > datetime.now(tz=UTC)
    assert token.deleted_at is None
    assert token.created_at.tzinfo == UTC


def test_access_token_expired_predicate_not_expired() -> None:
    user_id = UserId(uuid4())
    token = AccessToken.factory(
        user_id=user_id,
        expires_in=timedelta(days=7),
    )

    assert not token.expired_predicate()


def test_access_token_expired_predicate_expired_by_time() -> None:
    user_id = UserId(uuid4())
    token = AccessToken.factory(
        user_id=user_id,
        expires_in=timedelta(days=-1),
    )

    assert token.expired_predicate()


def test_access_token_expired_predicate_revoked() -> None:
    user_id = UserId(uuid4())
    token = AccessToken.factory(
        user_id=user_id,
        expires_in=timedelta(days=7),
    )
    token.revoke()

    assert token.expired_predicate()


def test_access_token_ensure_expired_raises_when_expired() -> None:
    user_id = UserId(uuid4())
    token = AccessToken.factory(
        user_id=user_id,
        expires_in=timedelta(days=-1),
    )

    with pytest.raises(AccessTokenExpiredError):
        token.ensure_expired()


def test_access_token_ensure_expired_raises_when_revoked() -> None:
    user_id = UserId(uuid4())
    token = AccessToken.factory(
        user_id=user_id,
        expires_in=timedelta(days=7),
    )
    token.revoke()

    with pytest.raises(AccessTokenExpiredError):
        token.ensure_expired()


def test_access_token_revoke() -> None:
    user_id = UserId(uuid4())
    token = AccessToken.factory(
        user_id=user_id,
        expires_in=timedelta(days=7),
    )

    token.revoke()

    assert token.revoked is True
    assert token.deleted_at is not None
    assert token.deleted_at.tzinfo == UTC
