"""
Tests for TokenRepository

TDD tests for admin token management.
"""

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

from backend.data.admin_models import AdminToken
from backend.data.repositories.token_repository import TokenRepository


class TestTokenRepository:
    """Tests for TokenRepository."""

    @pytest.fixture
    def sample_token(self) -> AdminToken:
        """Create a sample token for testing."""
        return AdminToken(
            token="test-token-123",
            name="Test Token",
            is_active=True,
            created_at=datetime.now(timezone.utc),
            last_used_at=None,
            expires_at=None,
        )

    @pytest.fixture
    def expired_token(self) -> AdminToken:
        """Create an expired token."""
        return AdminToken(
            token="expired-token",
            name="Expired Token",
            is_active=True,
            created_at=datetime.now(timezone.utc) - timedelta(days=30),
            expires_at=datetime.now(timezone.utc) - timedelta(days=1),
        )

    @pytest.fixture
    def inactive_token(self) -> AdminToken:
        """Create an inactive token."""
        return AdminToken(
            token="inactive-token",
            name="Inactive Token",
            is_active=False,
            created_at=datetime.now(timezone.utc),
        )

    @pytest.fixture
    def repo(self) -> TokenRepository:
        """Create a TokenRepository instance."""
        return TokenRepository()

    def test_is_valid_returns_true_for_active_token(self, repo, sample_token):
        """Test is_valid returns True for an active, non-expired token."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = sample_token
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            result = repo.is_valid("test-token-123")

            assert result is True
            mock_session.get.assert_called_once_with(AdminToken, "test-token-123")

    def test_is_valid_returns_false_for_nonexistent_token(self, repo):
        """Test is_valid returns False for a non-existent token."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = None
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            result = repo.is_valid("nonexistent-token")

            assert result is False

    def test_is_valid_returns_false_for_inactive_token(self, repo, inactive_token):
        """Test is_valid returns False for an inactive token."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = inactive_token
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            result = repo.is_valid("inactive-token")

            assert result is False

    def test_is_valid_returns_false_for_expired_token(self, repo, expired_token):
        """Test is_valid returns False for an expired token."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = expired_token
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            result = repo.is_valid("expired-token")

            assert result is False

    def test_get_returns_token_when_exists(self, repo, sample_token):
        """Test get returns token when it exists."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = sample_token
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            result = repo.get("test-token-123")

            assert result is not None
            assert result.token == "test-token-123"
            assert result.name == "Test Token"
            mock_session.expunge.assert_called_once_with(sample_token)

    def test_get_returns_none_when_not_exists(self, repo):
        """Test get returns None when token doesn't exist."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = None
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            result = repo.get("nonexistent-token")

            assert result is None

    def test_create_new_token(self, repo):
        """Test creating a new token."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = None  # Token doesn't exist
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            repo.create("new-token", "New Token", expires_at=None)

            mock_session.add.assert_called_once()
            added_token = mock_session.add.call_args[0][0]
            assert isinstance(added_token, AdminToken)
            assert added_token.token == "new-token"
            assert added_token.name == "New Token"

    def test_create_updates_existing_token(self, repo, sample_token):
        """Test create updates an existing token."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = sample_token
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            repo.create("test-token-123", "Updated Name", expires_at=None)

            mock_session.add.assert_called_once_with(sample_token)
            assert sample_token.name == "Updated Name"
            assert sample_token.is_active is True

    def test_update_usage(self, repo, sample_token):
        """Test updating token last_used_at timestamp."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = sample_token
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            repo.update_usage("test-token-123")

            assert sample_token.last_used_at is not None
            mock_session.add.assert_called_once_with(sample_token)

    def test_deactivate_returns_true_when_token_exists(self, repo, sample_token):
        """Test deactivate returns True when token exists."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = sample_token
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            result = repo.deactivate("test-token-123")

            assert result is True
            assert sample_token.is_active is False
            mock_session.add.assert_called_once_with(sample_token)

    def test_deactivate_returns_false_when_token_not_exists(self, repo):
        """Test deactivate returns False when token doesn't exist."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_session.get.return_value = None
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            result = repo.deactivate("nonexistent-token")

            assert result is False
