# app/modules/tenancy/tests/unit/test_user_password_reset_token.py
"""Unit tests for UserPasswordResetToken model."""

import uuid
from datetime import datetime, timedelta

import pytest

from app.modules.tenancy.models import User
from app.modules.tenancy.models.user_password_reset_token import UserPasswordResetToken


@pytest.mark.unit
@pytest.mark.database
class TestUserPasswordResetToken:
    """Test UserPasswordResetToken model CRUD operations."""

    @pytest.fixture
    def prt_user(self, db):
        """Create a test user for token tests."""
        uid = uuid.uuid4().hex[:8]
        user = User(
            email=f"prt_{uid}@test.com",
            username=f"prt_{uid}",
            hashed_password="hashed_password",  # noqa: SEC001
            role="merchant_owner",
            is_active=True,
            is_email_verified=True,
        )
        db.add(user)
        db.commit()
        db.refresh(user)
        return user

    def test_create_for_user(self, db, prt_user):
        """Test creating a reset token returns a plaintext token."""
        plaintext = UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        assert plaintext is not None
        assert len(plaintext) > 20

    def test_find_valid_token(self, db, prt_user):
        """Test finding a valid token by plaintext."""
        plaintext = UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        found = UserPasswordResetToken.find_valid_token(db, plaintext)
        assert found is not None
        assert found.user_id == prt_user.id
        assert found.used_at is None

    def test_find_valid_token_wrong_token(self, db, prt_user):
        """Test that a wrong token returns None."""
        UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        found = UserPasswordResetToken.find_valid_token(db, "wrong-token-value")
        assert found is None

    def test_mark_used(self, db, prt_user):
        """Test marking a token as used."""
        plaintext = UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        token = UserPasswordResetToken.find_valid_token(db, plaintext)
        assert token is not None

        token.mark_used(db)
        db.commit()

        assert token.used_at is not None

        # Token should no longer be findable
        found = UserPasswordResetToken.find_valid_token(db, plaintext)
        assert found is None

    def test_expired_token_not_found(self, db, prt_user):
        """Test that an expired token is not returned by find_valid_token."""
        plaintext = UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        # Manually expire the token
        token = UserPasswordResetToken.find_valid_token(db, plaintext)
        token.expires_at = datetime.utcnow() - timedelta(hours=1)
        db.commit()

        found = UserPasswordResetToken.find_valid_token(db, plaintext)
        assert found is None

    def test_create_invalidates_old_tokens(self, db, prt_user):
        """Test that creating a new token invalidates old unused tokens."""
        plaintext_1 = UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        plaintext_2 = UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        # Old token should be gone (deleted)
        found_1 = UserPasswordResetToken.find_valid_token(db, plaintext_1)
        assert found_1 is None

        # New token should work
        found_2 = UserPasswordResetToken.find_valid_token(db, plaintext_2)
        assert found_2 is not None

    def test_hash_token_deterministic(self):
        """Test that hash_token produces consistent results."""
        token = "test-token-value"
        hash1 = UserPasswordResetToken.hash_token(token)
        hash2 = UserPasswordResetToken.hash_token(token)
        assert hash1 == hash2
        assert len(hash1) == 64  # SHA256 hex digest

    def test_token_expiry_is_1_hour(self, db, prt_user):
        """Test that tokens are created with 1-hour expiry."""
        plaintext = UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        token = UserPasswordResetToken.find_valid_token(db, plaintext)
        assert token is not None

        # Expiry should be approximately 1 hour from now
        expected = datetime.utcnow() + timedelta(hours=1)
        delta = abs((token.expires_at - expected).total_seconds())
        assert delta < 60  # Within 60 seconds tolerance

    def test_token_repr(self, db, prt_user):
        """Test token __repr__."""
        plaintext = UserPasswordResetToken.create_for_user(db, prt_user.id)
        db.commit()

        token = UserPasswordResetToken.find_valid_token(db, plaintext)
        repr_str = repr(token)
        assert "UserPasswordResetToken" in repr_str
        assert str(token.user_id) in repr_str
