# ================================================================
# services/auth/tests/test_subscription_fetcher.py
# ================================================================
"""
Test suite for subscription fetcher functionality
"""

import pytest
from unittest.mock import Mock, AsyncMock, patch
from fastapi import HTTPException, status

from app.utils.subscription_fetcher import SubscriptionFetcher
from app.services.auth_service import EnhancedAuthService


class TestSubscriptionFetcher:
    """Tests for SubscriptionFetcher"""

    @pytest.mark.asyncio
    @pytest.mark.unit
    async def test_subscription_fetcher_correct_url(self):
        """Test that subscription fetcher uses the correct URL"""
        fetcher = SubscriptionFetcher("http://tenant-service:8000")
        
        # Mock httpx.AsyncClient to capture the URL being called
        with patch('httpx.AsyncClient') as mock_client_class:
            mock_client = AsyncMock()
            mock_client_class.return_value.__aenter__.return_value = mock_client
            
            # Mock the response
            mock_response = Mock()
            mock_response.status_code = 200
            mock_response.json.return_value = []
            mock_client.get.return_value = mock_response
            
            # Call the method
            try:
                await fetcher.get_user_subscription_context("test-user-id", "test-service-token")
            except Exception:
                pass  # We're just testing the URL, not the full flow
            
            # Verify the correct URL was called
            mock_client.get.assert_called_once()
            called_url = mock_client.get.call_args[0][0]
            
            # Should use the corrected URL
            assert called_url == "http://tenant-service:8000/api/v1/tenants/members/user/test-user-id"
            assert called_url != "http://tenant-service:8000/api/v1/users/test-user-id/memberships"

    @pytest.mark.asyncio
    @pytest.mark.unit
    async def test_service_token_creation(self):
        """Test that service tokens are created properly"""
        # Test the JWT handler directly
        from shared.auth.jwt_handler import JWTHandler
        
        handler = JWTHandler("test-secret-key")
        
        # Create a service token
        service_token = handler.create_service_token("auth-service")
        
        # Verify it's a valid JWT
        assert isinstance(service_token, str)
        assert len(service_token) > 0
        
        # Verify we can decode it (without verification for testing)
        import jwt
        decoded = jwt.decode(service_token, options={"verify_signature": False})
        
        # Verify service token structure
        assert decoded["type"] == "service"
        assert decoded["service"] == "auth-service"
        assert decoded["is_service"] is True
        assert decoded["role"] == "admin"

    @pytest.mark.asyncio
    @pytest.mark.unit
    async def test_auth_service_uses_correct_token(self):
        """Test that EnhancedAuthService uses proper service tokens"""
        # Mock the database manager
        mock_db_manager = Mock()
        mock_session = AsyncMock()
        mock_db_manager.get_session.return_value.__aenter__.return_value = mock_session
        
        # Create auth service
        auth_service = EnhancedAuthService(mock_db_manager)
        
        # Mock the JWT handler to capture calls
        with patch('app.core.security.SecurityManager.create_service_token') as mock_create_token:
            mock_create_token.return_value = "test-service-token"
            
            # Call the method that generates service tokens
            service_token = await auth_service._get_service_token()
            
            # Verify it was called correctly
            mock_create_token.assert_called_once_with("auth-service")
            assert service_token == "test-service-token"


class TestServiceTokenValidation:
    """Tests for service token validation in tenant service"""

    @pytest.mark.asyncio
    @pytest.mark.unit
    async def test_service_token_validation(self):
        """Test that service tokens are properly validated"""
        from shared.auth.jwt_handler import JWTHandler
        from shared.auth.decorators import extract_user_from_jwt
        
        # Create a service token
        handler = JWTHandler("test-secret-key")
        service_token = handler.create_service_token("auth-service")
        
        # Create a mock request with the service token
        mock_request = Mock()
        mock_request.headers = {
            "authorization": f"Bearer {service_token}"
        }
        
        # Extract user from JWT
        user_context = extract_user_from_jwt(f"Bearer {service_token}")
        
        # Verify service user context
        assert user_context is not None
        assert user_context["type"] == "service"
        assert user_context["is_service"] is True
        assert user_context["role"] == "admin"
        assert user_context["service"] == "auth-service"


class TestIntegrationFlow:
    """Integration tests for the complete login flow"""

    @pytest.mark.asyncio
    @pytest.mark.integration
    async def test_complete_login_flow_mocked(self):
        """Test the complete login flow with mocked services"""
        # Mock database manager
        mock_db_manager = Mock()
        mock_session = AsyncMock()
        mock_db_manager.get_session.return_value.__aenter__.return_value = mock_session
        
        # Create auth service
        auth_service = EnhancedAuthService(mock_db_manager)
        
        # Mock user authentication
        mock_user = Mock()
        mock_user.id = "test-user-id"
        mock_user.email = "test@bakery.es"
        mock_user.full_name = "Test User"
        mock_user.is_active = True
        mock_user.is_verified = True
        mock_user.role = "admin"
        
        # Mock repositories
        mock_user_repo = AsyncMock()
        mock_user_repo.authenticate_user.return_value = mock_user
        mock_user_repo.update_last_login.return_value = None
        
        mock_token_repo = AsyncMock()
        mock_token_repo.revoke_all_user_tokens.return_value = None
        mock_token_repo.create_token.return_value = None
        
        # Mock UnitOfWork
        mock_uow = AsyncMock()
        mock_uow.register_repository.side_effect = lambda name, repo_class, model: {
            "users": mock_user_repo,
            "tokens": mock_token_repo
        }[name]
        mock_uow.commit.return_value = None
        
        # Mock subscription fetcher
        with patch('app.utils.subscription_fetcher.SubscriptionFetcher') as mock_fetcher_class:
            mock_fetcher = AsyncMock()
            mock_fetcher_class.return_value = mock_fetcher
            
            # Mock subscription data
            mock_fetcher.get_user_subscription_context.return_value = {
                "tenant_id": "test-tenant-id",
                "tenant_role": "owner",
                "subscription": {
                    "tier": "professional",
                    "status": "active",
                    "valid_until": "2025-02-15T00:00:00Z"
                },
                "tenant_access": []
            }
            
            # Mock service token generation
            with patch.object(auth_service, '_get_service_token', return_value="test-service-token"):
                
                # Mock SecurityManager methods
                with patch('app.core.security.SecurityManager.create_access_token', return_value="access-token"):
                    with patch('app.core.security.SecurityManager.create_refresh_token', return_value="refresh-token"):
                        
                        # Create login data
                        from app.schemas.auth import UserLogin
                        login_data = UserLogin(
                            email="test@bakery.es",
                            password="password123"
                        )
                        
                        # Call login
                        result = await auth_service.login_user(login_data)
                        
                        # Verify the result
                        assert result is not None
                        assert result.access_token == "access-token"
                        assert result.refresh_token == "refresh-token"
                        
                        # Verify subscription fetcher was called with correct URL
                        mock_fetcher.get_user_subscription_context.assert_called_once()
                        call_args = mock_fetcher.get_user_subscription_context.call_args
                        
                        # Check that the fetcher was initialized with correct URL
                        fetcher_init_call = mock_fetcher_class.call_args
                        assert "tenant-service:8000" in str(fetcher_init_call)
                        
                        # Verify service token was used
                        assert call_args[1]["service_token"] == "test-service-token"


class TestErrorHandling:
    """Tests for error handling in subscription fetching"""

    @pytest.mark.asyncio
    @pytest.mark.unit
    async def test_subscription_fetcher_404_handling(self):
        """Test handling of 404 errors from tenant service"""
        fetcher = SubscriptionFetcher("http://tenant-service:8000")
        
        with patch('httpx.AsyncClient') as mock_client_class:
            mock_client = AsyncMock()
            mock_client_class.return_value.__aenter__.return_value = mock_client
            
            # Mock 404 response
            mock_response = Mock()
            mock_response.status_code = 404
            mock_client.get.return_value = mock_response
            
            # This should raise an HTTPException
            with pytest.raises(HTTPException) as exc_info:
                await fetcher.get_user_subscription_context("test-user-id", "test-service-token")
            
            assert exc_info.value.status_code == 500
            assert "Failed to fetch user memberships" in str(exc_info.value.detail)

    @pytest.mark.asyncio
    @pytest.mark.unit
    async def test_subscription_fetcher_500_handling(self):
        """Test handling of 500 errors from tenant service"""
        fetcher = SubscriptionFetcher("http://tenant-service:8000")
        
        with patch('httpx.AsyncClient') as mock_client_class:
            mock_client = AsyncMock()
            mock_client_class.return_value.__aenter__.return_value = mock_client
            
            # Mock 500 response
            mock_response = Mock()
            mock_response.status_code = 500
            mock_client.get.return_value = mock_response
            
            # This should raise an HTTPException
            with pytest.raises(HTTPException) as exc_info:
                await fetcher.get_user_subscription_context("test-user-id", "test-service-token")
            
            assert exc_info.value.status_code == 500
            assert "Failed to fetch user memberships" in str(exc_info.value.detail)


class TestUrlCorrection:
    """Tests to verify the URL correction is working"""

    @pytest.mark.unit
    def test_url_pattern_correction(self):
        """Test that the URL pattern is correctly fixed"""
        # This test documents the fix that was made
        
        # OLD (incorrect) URL pattern
        old_url = "http://tenant-service:8000/api/v1/users/{user_id}/memberships"
        
        # NEW (correct) URL pattern
        new_url = "http://tenant-service:8000/api/v1/tenants/members/user/{user_id}"
        
        # Verify they're different
        assert old_url != new_url
        
        # Verify the new URL follows the correct pattern
        assert "/api/v1/tenants/" in new_url
        assert "/members/user/" in new_url
        assert "{user_id}" in new_url
        
        # Verify the old URL is not used
        assert "/api/v1/users/" not in new_url
        assert "/memberships" not in new_url