"""
Storm Logger 2.0 - Django Integration Tests
Comprehensive testing of Django integration features
"""

import os
from unittest.mock import Mock, patch

import django
import pytest

# Setup Django before any imports
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.django_hooks.settings")
django.setup()

from django.contrib.auth.models import User
from django.core.management import call_command
from django.db import connection
from django.test import Client, TestCase

from storm_logger import DjangoStormLoggerConfig, LogLevel, get_logger
from storm_logger.integrations.django_config import get_django_config
from tests.django_hooks.test_app.models import BusinessMetric, TestModel, UserProfile

@pytest.mark.django
class TestDjangoStormLoggerConfig:
    """Test Django Storm Logger configuration."""
    
    def test_basic_django_config(self):
        """Test basic Django configuration."""
        config = DjangoStormLoggerConfig()
        
        assert config.log_level == LogLevel.INFO
        assert config.enable_test_logging is True
        assert config.enable_business_metrics is True
        assert config.track_performance_metrics is True
        
    def test_django_config_with_custom_settings(self):
        """Test Django configuration with custom settings."""
        config = DjangoStormLoggerConfig(
            log_level=LogLevel.DEBUG,
            enable_test_logging=False,
            app_log_levels={
                "myapp": LogLevel.WARNING,
                "django.request": LogLevel.ERROR,
            },
            excluded_apps=["django.contrib.admin"]
        )
        
        assert config.log_level == LogLevel.DEBUG
        assert config.enable_test_logging is False
        assert config.app_log_levels["myapp"] == LogLevel.WARNING
        assert "django.contrib.admin" in config.excluded_apps
        
    def test_django_settings_generation(self):
        """Test Django settings generation."""
        config = DjangoStormLoggerConfig()
        django_settings = config.for_django_settings()
        
        assert "version" in django_settings
        assert "handlers" in django_settings
        assert "loggers" in django_settings
        assert "formatters" in django_settings
        
        # Check handlers
        assert "console" in django_settings["handlers"]
        assert "app_file" in django_settings["handlers"]
        assert "error_file" in django_settings["handlers"]
        
        # Check loggers
        assert "" in django_settings["loggers"]  # Root logger
        assert "django" in django_settings["loggers"]
        
    def test_environment_specific_configs(self):
        """Test environment-specific configurations."""
        production_config = DjangoStormLoggerConfig.for_production()
        testing_config = DjangoStormLoggerConfig.for_testing()
        
        # Production should be more restrictive
        assert production_config.log_level == LogLevel.WARNING
        assert production_config.enable_test_logging is False
        
        # Testing should be more verbose
        assert testing_config.log_level == LogLevel.DEBUG
        assert testing_config.enable_test_logging is True


@pytest.mark.django
class TestDjangoIntegration(TestCase):
    """Test Django integration with Storm Logger."""
    
    def setUp(self):
        """Set up test environment."""
        self.client = Client()
        self.logger = get_logger(__name__)
        
    def test_logger_in_view(self):
        """Test logger functionality in Django views."""
        response = self.client.get("/test/")
        
        assert response.status_code == 200
        data = response.json()
        assert "message" in data
        assert "execution_time" in data
        
    def test_error_logging_in_view(self):
        """Test error logging in Django views."""
        with pytest.raises(ValueError):
            self.client.get("/test/error/")
            
    def test_slow_view_performance_logging(self):
        """Test performance logging for slow views."""
        response = self.client.get("/test/slow/")
        
        assert response.status_code == 200
        data = response.json()
        assert data["execution_time"] > 0.04  # Should be at least 40ms (view sleeps for 50ms)
        assert "warning" in data
        
    def test_model_logging(self):
        """Test logging in Django models."""
        # Create a test model
        model = TestModel.objects.create(name="Test Model", value=42)
        
        assert model.id is not None
        assert model.name == "Test Model"
        assert model.value == 42
        
        # Update the model
        model.value = 100
        model.save()
        
        # Delete the model
        model.delete()
        
    def test_business_metrics_view(self):
        """Test business metrics tracking in views."""
        response = self.client.get("/test/metrics/")
        
        assert response.status_code == 200
        data = response.json()
        assert data["metrics_tracked"] == 3
        assert "processing_time" in data
        
    def test_security_logging(self):
        """Test security event logging."""
        # Normal security test
        response = self.client.get("/test/security/")
        assert response.status_code == 200
        assert response.json()["status"] == "secure"
        
        # Suspicious activity test
        response = self.client.get("/test/security/?suspicious=true")
        assert response.status_code == 200
        assert "Suspicious activity detected" in response.json()["message"]
        
    def test_user_authentication_logging(self):
        """Test user authentication logging."""
        # Create a test user
        user = User.objects.create_user(
            username="testuser",
            password="testpass123"
        )
        
        # Test unauthenticated access
        response = self.client.get("/test/authenticated/")
        assert response.status_code == 302  # Redirect to login
        
        # Test authenticated access
        self.client.login(username="testuser", password="testpass123")
        response = self.client.get("/test/authenticated/")
        assert response.status_code == 200
        assert response.json()["user"] == "testuser"
        
    def test_cached_view_logging(self):
        """Test logging with cached views."""
        # First request
        response1 = self.client.get("/test/cached/")
        assert response1.status_code == 200
        
        # Second request (should be cached)
        response2 = self.client.get("/test/cached/")
        assert response2.status_code == 200
        
        # Both should have the same content
        assert response1.json()["message"] == response2.json()["message"]
        
    def test_model_list_view_logging(self):
        """Test logging in model list views."""
        # Create test models
        TestModel.objects.create(name="Model 1", value=1)
        TestModel.objects.create(name="Model 2", value=2)
        
        response = self.client.get("/test/models/")
        assert response.status_code == 200
        
    def test_model_detail_view_logging(self):
        """Test logging in model detail views."""
        model = TestModel.objects.create(name="Detail Model", value=123)
        
        response = self.client.get(f"/test/models/{model.id}/")
        assert response.status_code == 200
        
    def test_create_model_logging(self):
        """Test logging when creating models via API."""
        response = self.client.post("/test/create/")
        
        assert response.status_code == 200
        data = response.json()
        assert "model_id" in data
        assert "model_name" in data
        
        # Verify model was created
        model = TestModel.objects.get(id=data["model_id"])
        assert model.name == data["model_name"]


@pytest.mark.django
class TestDjangoPerformanceLogging:
    """Test Django performance logging features."""
    
    def test_database_query_logging(self, settings):
        """Test database query logging."""
        # Create a model (triggers database query)
        model = TestModel.objects.create(name="Performance Test", value=99)
        
        # Verify model was created
        assert model.id is not None
        assert model.name == "Performance Test"
            
    def test_middleware_performance_logging(self, settings):
        """Test middleware performance logging."""
        client = Client()
        
        response = client.get("/test/")
        
        # Check response
        assert response.status_code == 200
        data = response.json()
        assert "execution_time" in data
            
    def test_slow_query_detection(self, settings):
        """Test slow query detection and logging."""
        # Create model without mocking
        model = TestModel.objects.create(name="Slow Query Test", value=1)
        
        # Verify model was created
        assert model.id is not None
        assert model.name == "Slow Query Test"


@pytest.mark.django
class TestDjangoBusinessMetrics:
    """Test Django business metrics features."""
    
    def test_user_action_tracking(self, settings):
        """Test user action tracking."""
        user = User.objects.create_user(
            username="metricsuser",
            password="testpass123"
        )
        
        client = Client()
        client.login(username="metricsuser", password="testpass123")
        
        response = client.get("/test/authenticated/")
        
        assert response.status_code == 200
        data = response.json()
        assert data["user"] == "metricsuser"
            
    def test_business_metric_collection(self, settings):
        """Test business metric collection."""
        client = Client()
        
        response = client.get("/test/metrics/")
        
        assert response.status_code == 200
        
        # Check response data
        data = response.json()
        assert data["metrics_tracked"] == 3
            
    def test_custom_business_metrics(self, settings):
        """Test custom business metrics."""
        logger = get_logger(__name__)
        
        # Test different types of business metrics - should not raise exceptions
        try:
            logger.business_metric("custom_metric", 100)
            logger.business_metric("revenue_metric", 250.50, context={"currency": "USD"})
            logger.business_metric("user_engagement", 85.5, threshold=80.0)
            success = True
        except Exception:
            success = False
        
        assert success


@pytest.mark.django
class TestDjangoErrorHandling:
    """Test Django error handling and logging."""
    
    def test_view_error_logging(self, settings):
        """Test error logging in views."""
        client = Client()
        
        # Django's test client re-raises exceptions by default
        # We need to catch the exception
        with pytest.raises(ValueError, match="Test error for Storm Logger testing"):
            client.get("/test/error/")
            
    def test_model_error_logging(self, settings):
        """Test error logging in models."""
        # Try to create an invalid model
        with pytest.raises(Exception):
            # Name too long (assuming max_length on model)
            TestModel.objects.create(name="x" * 200, value="invalid")
            
    def test_database_error_logging(self, settings):
        """Test database error logging."""
        # Try to create a model with invalid data
        with pytest.raises(Exception):
            # Invalid value type
            TestModel.objects.create(name=None, value="not_an_int")


@pytest.mark.django
class TestDjangoConfigValidation:
    """Test Django configuration validation."""
    
    def test_config_validation(self):
        """Test configuration validation."""
        config = DjangoStormLoggerConfig()
        
        # Test valid configuration
        errors = config.validate()
        assert errors == []  # Empty list means no errors
        
        # Test invalid log level
        with pytest.raises(ValueError, match="'INVALID' is not a valid LogLevel"):
            DjangoStormLoggerConfig(log_level="INVALID")
            
    def test_app_log_levels_validation(self):
        """Test app log levels validation."""
        # Valid app log levels
        config = DjangoStormLoggerConfig(
            app_log_levels={"myapp": LogLevel.DEBUG}
        )
        errors = config.validate()
        assert errors == []  # Empty list means no errors
        
        # Invalid app log levels - will be caught during validation
        config = DjangoStormLoggerConfig(
            app_log_levels={"myapp": "INVALID"}
        )
        errors = config.validate()
        assert len(errors) > 0
        assert "Invalid log level: INVALID" in errors[0]
            
    def test_excluded_apps_validation(self):
        """Test excluded apps validation."""
        config = DjangoStormLoggerConfig(
            excluded_apps=["django.contrib.admin", "myapp"]
        )
        errors = config.validate()
        assert errors == []  # Empty list means no errors
        
        # Invalid excluded apps (should be a list) - will be caught during validation
        config = DjangoStormLoggerConfig(excluded_apps="django.contrib.admin")
        errors = config.validate()
        assert len(errors) > 0
        assert "excluded_apps must be a list" in errors[0]


@pytest.mark.django
class TestDjangoMiddleware:
    """Test Django middleware integration."""
    
    def test_request_logging_middleware(self, settings):
        """Test request logging middleware."""
        client = Client()
        
        response = client.get("/test/")
        
        assert response.status_code == 200
        # Verify response structure
        data = response.json()
        assert "message" in data
            
    def test_response_logging_middleware(self, settings):
        """Test response logging middleware."""
        client = Client()
        
        response = client.get("/test/")
        
        assert response.status_code == 200
        # Verify response has expected fields
        data = response.json()
        assert "timestamp" in data
            
    def test_exception_logging_middleware(self, settings):
        """Test exception logging middleware."""
        client = Client()
        
        # Django's test client re-raises exceptions by default
        with pytest.raises(ValueError, match="Test error for Storm Logger testing"):
            client.get("/test/error/")


if __name__ == "__main__":
    pytest.main([__file__, "-v"])