"""
Tests for Admin Authentication.
"""

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

from fastapi import HTTPException

from backend.data.repositories import TokenRepository
from backend.data.admin_models import AdminToken
from backend.web.core.auth import (
    get_token_repository,
    reset_token_repository,
    validate_admin_token,
)


@pytest.fixture
def mock_token_repo():
    """Create a mock TokenRepository."""
    repo = MagicMock(spec=TokenRepository)
    repo.is_valid.return_value = True
    return repo


@pytest.fixture(autouse=True)
def reset_repo():
    """Reset token repository after each test."""
    yield
    reset_token_repository()


class TestValidateAdminToken:
    """Tests for validate_admin_token dependency."""

    def test_missing_token_raises_401(self, mock_token_repo):
        """Test that missing token raises 401."""
        import asyncio

        with pytest.raises(HTTPException) as exc_info:
            asyncio.get_event_loop().run_until_complete(
                validate_admin_token(None, mock_token_repo)
            )

        assert exc_info.value.status_code == 401
        assert "Admin token required" in exc_info.value.detail

    def test_invalid_token_raises_401(self, mock_token_repo):
        """Test that invalid token raises 401."""
        import asyncio

        mock_token_repo.is_valid.return_value = False

        with pytest.raises(HTTPException) as exc_info:
            asyncio.get_event_loop().run_until_complete(
                validate_admin_token("invalid-token", mock_token_repo)
            )

        assert exc_info.value.status_code == 401
        assert "Invalid or expired" in exc_info.value.detail

    def test_valid_token_returns_token(self, mock_token_repo):
        """Test that valid token is returned."""
        import asyncio

        token = "valid-test-token"
        mock_token_repo.is_valid.return_value = True

        result = asyncio.get_event_loop().run_until_complete(
            validate_admin_token(token, mock_token_repo)
        )

        assert result == token
        mock_token_repo.update_usage.assert_called_once_with(token)


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

    def test_is_valid_active_token(self):
        """Test valid active token."""
        with patch("backend.data.repositories.token_repository.BaseRepository._session") as mock_ctx:
            mock_session = MagicMock()
            mock_ctx.return_value.__enter__.return_value = mock_session

            mock_token = AdminToken(
                token="test-token",
                name="Test",
                is_active=True,
                expires_at=None,
            )
            mock_session.get.return_value = mock_token

            repo = TokenRepository()
            assert repo.is_valid("test-token") is True

    def test_is_valid_inactive_token(self):
        """Test inactive token."""
        with patch("backend.data.repositories.token_repository.BaseRepository._session") as mock_ctx:
            mock_session = MagicMock()
            mock_ctx.return_value.__enter__.return_value = mock_session

            mock_token = AdminToken(
                token="test-token",
                name="Test",
                is_active=False,
                expires_at=None,
            )
            mock_session.get.return_value = mock_token

            repo = TokenRepository()
            assert repo.is_valid("test-token") is False

    def test_is_valid_expired_token(self):
        """Test expired token."""
        with patch("backend.data.repositories.token_repository.BaseRepository._session") as mock_ctx:
            mock_session = MagicMock()
            mock_ctx.return_value.__enter__.return_value = mock_session

            mock_token = AdminToken(
                token="test-token",
                name="Test",
                is_active=True,
                expires_at=datetime.utcnow() - timedelta(days=1),
            )
            mock_session.get.return_value = mock_token

            repo = TokenRepository()
            # Need to also mock _now() to ensure proper comparison
            with patch.object(repo, "_now", return_value=datetime.utcnow()):
                assert repo.is_valid("test-token") is False

    def test_is_valid_expired_token_timezone_aware(self):
        """Test expired token with timezone-aware datetime.

        This verifies the fix for comparing timezone-aware and naive datetimes.
        The auth API now creates tokens with timezone-aware expiration dates.
        """
        with patch("backend.data.repositories.token_repository.BaseRepository._session") as mock_ctx:
            mock_session = MagicMock()
            mock_ctx.return_value.__enter__.return_value = mock_session

            # Create token with timezone-aware expiration (as auth API now does)
            mock_token = AdminToken(
                token="test-token",
                name="Test",
                is_active=True,
                expires_at=datetime.now(timezone.utc) - timedelta(days=1),
            )
            mock_session.get.return_value = mock_token

            repo = TokenRepository()
            # _now() returns timezone-aware datetime, should compare correctly
            assert repo.is_valid("test-token") is False

    def test_is_valid_not_expired_token_timezone_aware(self):
        """Test non-expired token with timezone-aware datetime."""
        with patch("backend.data.repositories.token_repository.BaseRepository._session") as mock_ctx:
            mock_session = MagicMock()
            mock_ctx.return_value.__enter__.return_value = mock_session

            # Create token with timezone-aware expiration in the future
            mock_token = AdminToken(
                token="test-token",
                name="Test",
                is_active=True,
                expires_at=datetime.now(timezone.utc) + timedelta(days=1),
            )
            mock_session.get.return_value = mock_token

            repo = TokenRepository()
            assert repo.is_valid("test-token") is True

    def test_is_valid_token_not_found(self):
        """Test token not found."""
        with patch("backend.data.repositories.token_repository.BaseRepository._session") as mock_ctx:
            mock_session = MagicMock()
            mock_ctx.return_value.__enter__.return_value = mock_session
            mock_session.get.return_value = None

            repo = TokenRepository()
            assert repo.is_valid("nonexistent") is False


class TestGetTokenRepository:
    """Tests for get_token_repository function."""

    def test_returns_singleton(self):
        """Test that get_token_repository returns singleton."""
        reset_token_repository()

        repo1 = get_token_repository()
        repo2 = get_token_repository()

        assert repo1 is repo2

    def test_reset_clears_singleton(self):
        """Test that reset clears singleton."""
        repo1 = get_token_repository()
        reset_token_repository()
        repo2 = get_token_repository()

        assert repo1 is not repo2
