"""
Performance tests for activities app.
"""
import time
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.db import connection
from django.test.utils import override_settings
from django.utils import timezone

from apps.activities.models import Activity, ActivityCategory
from apps.activities.utils import log_activity

User = get_user_model()


class ActivityPerformanceTest(TestCase):
    """Test performance of activity logging."""
    
    def setUp(self):
        """Set up test data."""
        self.user = User.objects.create_user(
            email='test@example.com',
            username='testuser',
            password='testpass123'
        )
        
        self.category = ActivityCategory.objects.create(
            name='Performance',
            code='performance',
            description='Performance test activities',
            color='#007bff',
            icon='fas fa-tachometer-alt',
            is_system=True
        )
    
    def test_bulk_activity_logging_performance(self):
        """Test performance of bulk activity logging."""
        start_time = time.time()
        
        # Log 100 activities
        for i in range(100):
            log_activity(
                user=self.user,
                action=f'BULK_TEST_{i}',
                category=self.category,
                metadata={'index': i}
            )
        
        end_time = time.time()
        duration = end_time - start_time
        
        # Should complete within reasonable time (adjust as needed)
        self.assertLess(duration, 5.0, f"Bulk logging took {duration:.2f} seconds")
        
        # Verify all activities were created
        self.assertEqual(Activity.objects.count(), 100)
    
    def test_query_performance_with_indexes(self):
        """Test query performance with proper indexes."""
        # Create test activities
        for i in range(50):
            log_activity(
                user=self.user,
                action=f'QUERY_TEST_{i}',
                category=self.category,
                ip_address='127.0.0.1',
                user_agent='Test Browser'
            )
        
        # Test query performance
        with self.assertNumQueries(1):
            activities = list(Activity.objects.for_user(self.user)[:10])
            self.assertEqual(len(activities), 10)
        
        with self.assertNumQueries(1):
            activities = list(Activity.objects.by_category(self.category)[:10])
            self.assertEqual(len(activities), 10)
        
        with self.assertNumQueries(1):
            activities = list(Activity.objects.recent(hours=1)[:10])
            self.assertEqual(len(activities), 10)
    
    def test_metadata_search_performance(self):
        """Test performance of metadata searches."""
        # Create activities with various metadata
        for i in range(20):
            log_activity(
                user=self.user,
                action=f'METADATA_TEST_{i}',
                category=self.category,
                metadata={
                    'type': 'test',
                    'index': i,
                    'group': i % 5
                }
            )
        
        # Test metadata filtering
        start_time = time.time()
        
        # Test JSON field queries
        activities = Activity.objects.filter(metadata__type='test')
        self.assertEqual(activities.count(), 20)
        
        activities = Activity.objects.filter(metadata__group=0)
        self.assertEqual(activities.count(), 4)
        
        end_time = time.time()
        duration = end_time - start_time
        
        # Should complete quickly
        self.assertLess(duration, 1.0, f"Metadata queries took {duration:.2f} seconds")
    
    def test_large_dataset_pagination(self):
        """Test pagination performance with large datasets."""
        # Create a larger dataset
        activities = []
        for i in range(200):
            activities.append(Activity(
                user=self.user,
                action=f'PAGINATION_TEST_{i}',
                category=self.category,
                description=f'Test activity {i}',
                created_at=timezone.now()
            ))
        
        Activity.objects.bulk_create(activities)
        
        # Test pagination performance
        start_time = time.time()
        
        # First page
        page1 = Activity.objects.order_by('-created_at')[:25]
        list(page1)  # Force evaluation
        
        # Second page
        page2 = Activity.objects.order_by('-created_at')[25:50]
        list(page2)  # Force evaluation
        
        # Third page
        page3 = Activity.objects.order_by('-created_at')[50:75]
        list(page3)  # Force evaluation
        
        end_time = time.time()
        duration = end_time - start_time
        
        # Should complete within reasonable time
        self.assertLess(duration, 2.0, f"Pagination took {duration:.2f} seconds")
    
    def test_concurrent_logging_simulation(self):
        """Test simulation of concurrent activity logging."""
        import threading
        
        def log_activities(thread_id):
            """Log activities in a separate thread."""
            for i in range(10):
                log_activity(
                    user=self.user,
                    action=f'CONCURRENT_TEST_{thread_id}_{i}',
                    category=self.category,
                    metadata={'thread_id': thread_id, 'index': i}
                )
        
        # Create multiple threads
        threads = []
        for thread_id in range(5):
            thread = threading.Thread(target=log_activities, args=(thread_id,))
            threads.append(thread)
        
        start_time = time.time()
        
        # Start all threads
        for thread in threads:
            thread.start()
        
        # Wait for all threads to complete
        for thread in threads:
            thread.join()
        
        end_time = time.time()
        duration = end_time - start_time
        
        # Should complete within reasonable time
        self.assertLess(duration, 3.0, f"Concurrent logging took {duration:.2f} seconds")
        
        # Verify all activities were created
        self.assertEqual(Activity.objects.count(), 50)
    
    def test_database_connection_efficiency(self):
        """Test database connection efficiency."""
        # Reset query count
        connection.queries_log.clear()
        
        # Log multiple activities
        for i in range(10):
            log_activity(
                user=self.user,
                action=f'CONNECTION_TEST_{i}',
                category=self.category
            )
        
        # Should use minimal database queries
        # Each log_activity call should use approximately 1-2 queries
        query_count = len(connection.queries)
        self.assertLess(query_count, 25, f"Used {query_count} queries for 10 activities")
    
    @override_settings(DEBUG=True)
    def test_memory_usage_monitoring(self):
        """Test memory usage during activity logging."""
        import gc
        import sys
        
        # Force garbage collection
        gc.collect()
        
        # Get initial memory usage (approximate)
        initial_objects = len(gc.get_objects())
        
        # Log many activities
        for i in range(100):
            log_activity(
                user=self.user,
                action=f'MEMORY_TEST_{i}',
                category=self.category,
                metadata={'large_data': 'x' * 1000}  # 1KB of data per activity
            )
        
        # Force garbage collection again
        gc.collect()
        
        # Check memory usage
        final_objects = len(gc.get_objects())
        object_increase = final_objects - initial_objects
        
        # Should not create excessive objects
        self.assertLess(object_increase, 5000, 
                       f"Created {object_increase} objects for 100 activities")
    
    def test_index_effectiveness(self):
        """Test effectiveness of database indexes."""
        # Create test data
        for i in range(100):
            log_activity(
                user=self.user,
                action=f'INDEX_TEST_{i % 10}',
                category=self.category,
                ip_address=f'192.168.1.{i % 20}',
                metadata={'group': i % 5}
            )
        
        # Test queries that should use indexes
        test_queries = [
            # User index
            lambda: Activity.objects.filter(user=self.user).count(),
            # Category index
            lambda: Activity.objects.filter(category=self.category).count(),
            # Created_at index
            lambda: Activity.objects.filter(
                created_at__gte=timezone.now() - timezone.timedelta(hours=1)
            ).count(),
            # Action index
            lambda: Activity.objects.filter(action='INDEX_TEST_1').count(),
            # IP address index
            lambda: Activity.objects.filter(ip_address='192.168.1.1').count(),
        ]
        
        for query_func in test_queries:
            with self.subTest(query=query_func):
                start_time = time.time()
                result = query_func()
                end_time = time.time()
                duration = end_time - start_time
                
                # Each query should complete quickly
                self.assertLess(duration, 0.1, 
                               f"Query took {duration:.4f} seconds, result: {result}")
