"""
Simple Performance Tests for Reports System

Tests query optimization and caching functionality without requiring
large dataset creation.
"""

from django.test import TestCase
from django.contrib.auth import get_user_model
from django.utils import timezone
from datetime import timedelta
from decimal import Decimal

from loans.models import Loan, LoanApplication, LoanProduct
from users.models import CustomUser, Branch
from reports.query_optimizer import QueryOptimizer
from reports.cache_service import CacheService
from reports.filter_service import ReportFilterService

User = get_user_model()


class SimplePerformanceTestCase(TestCase):
    """Simple performance tests for query optimization and caching"""
    
    def setUp(self):
        """Set up minimal test data"""
        # Create test branch
        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TB001',
            is_active=True
        )
        
        # Create test loan product
        self.loan_product = LoanProduct.objects.create(
            name='Test Product',
            interest_rate=10.0,
            processing_fee_rate=2.0,
            is_active=True
        )
        
        # Create test borrower
        self.borrower = User.objects.create_user(
            username='test_borrower',
            email='borrower@test.com',
            password='testpass123',
            role='borrower',
            first_name='Test',
            last_name='Borrower',
            phone_number='0700000001',
            branch=self.branch
        )
        
        # Create test loan application
        self.application = LoanApplication.objects.create(
            borrower=self.borrower,
            loan_product=self.loan_product,
            requested_amount=Decimal('10000.00'),
            status='approved'
        )
        
        # Create test loan
        self.loan = Loan.objects.create(
            borrower=self.borrower,
            application=self.application,
            loan_number='LN000001',
            principal_amount=Decimal('10000.00'),
            interest_amount=Decimal('1000.00'),
            processing_fee=Decimal('200.00'),
            total_amount=Decimal('11200.00'),
            disbursement_date=timezone.now().date() - timedelta(days=30),
            due_date=timezone.now().date() + timedelta(days=30),
            status='active',
            is_deleted=False,
            is_rolled_over=False
        )
    
    def test_query_optimizer_loans_queryset(self):
        """Test that query optimizer properly adds select_related and prefetch_related"""
        # Get optimized queryset
        optimized_qs = QueryOptimizer.get_optimized_loans_queryset()
        
        # Check that queryset is valid
        self.assertIsNotNone(optimized_qs)
        
        # Fetch a loan and verify related data is accessible without additional queries
        loan = optimized_qs.first()
        if loan:
            # These should not trigger additional queries due to select_related
            borrower_name = loan.borrower.get_full_name()
            branch_name = loan.borrower.branch.name if loan.borrower.branch else None
            product_name = loan.application.loan_product.name if loan.application else None
            
            self.assertIsNotNone(borrower_name)
            print(f"✓ Query optimizer working: {borrower_name}, {branch_name}, {product_name}")
    
    def test_query_optimizer_clients_queryset(self):
        """Test that query optimizer properly optimizes client querysets"""
        # Get optimized queryset
        optimized_qs = QueryOptimizer.get_optimized_clients_queryset()
        
        # Check that queryset is valid
        self.assertIsNotNone(optimized_qs)
        
        # Fetch a client and verify related data is accessible
        client = optimized_qs.first()
        if client:
            branch_name = client.branch.name if client.branch else None
            loans_count = client.loans.count()
            
            self.assertIsNotNone(client)
            print(f"✓ Client query optimizer working: {client.username}, loans: {loans_count}")
    
    def test_cache_service_basic_operations(self):
        """Test basic cache service operations"""
        # Test cache key generation
        cache_key = CacheService.generate_cache_key('test_prefix', 'arg1', 'arg2', key='value')
        self.assertIsNotNone(cache_key)
        self.assertIn('test_prefix', cache_key)
        print(f"✓ Cache key generated: {cache_key}")
        
        # Test cache set and get
        test_data = {'test': 'data', 'value': 123}
        success = CacheService.set_cached_data(cache_key, test_data, timeout=60)
        self.assertTrue(success)
        
        cached_data = CacheService.get_cached_data(cache_key)
        self.assertEqual(cached_data, test_data)
        print(f"✓ Cache set and get working: {cached_data}")
        
        # Test cache invalidation
        success = CacheService.invalidate_cache(cache_key)
        self.assertTrue(success)
        
        cached_data = CacheService.get_cached_data(cache_key)
        self.assertIsNone(cached_data)
        print("✓ Cache invalidation working")
    
    def test_cache_service_client_metrics(self):
        """Test caching of client metrics"""
        # Define a simple calculator function
        call_count = [0]  # Use list to allow modification in nested function
        
        def calculate_metrics():
            call_count[0] += 1
            return {
                'total_clients': 10,
                'active_portfolio': 5,
                'avg_score': 75.5
            }
        
        # First call should execute calculator
        result1 = CacheService.get_or_set_client_metrics(
            str(self.branch.id),
            calculate_metrics,
            timeout=60
        )
        self.assertEqual(call_count[0], 1)
        self.assertEqual(result1['total_clients'], 10)
        print(f"✓ First call executed calculator: {result1}")
        
        # Second call should use cache
        result2 = CacheService.get_or_set_client_metrics(
            str(self.branch.id),
            calculate_metrics,
            timeout=60
        )
        self.assertEqual(call_count[0], 1)  # Should still be 1 (not called again)
        self.assertEqual(result2, result1)
        print("✓ Second call used cache (calculator not called)")
        
        # Invalidate and verify
        CacheService.invalidate_client_metrics(str(self.branch.id))
        
        result3 = CacheService.get_or_set_client_metrics(
            str(self.branch.id),
            calculate_metrics,
            timeout=60
        )
        self.assertEqual(call_count[0], 2)  # Should be called again
        print("✓ After invalidation, calculator called again")
    
    def test_filter_service_with_optimization(self):
        """Test that filter service works with optimized querysets"""
        # Get base queryset
        base_qs = Loan.objects.all()
        
        # Apply optimization
        optimized_qs = QueryOptimizer.get_optimized_loans_queryset(base_qs)
        
        # Apply filters
        filtered_qs = ReportFilterService.apply_loan_status_filter(
            optimized_qs,
            exclude_rolled_over=True,
            exclude_deleted=True
        )
        
        # Verify results
        count = filtered_qs.count()
        self.assertGreaterEqual(count, 0)
        print(f"✓ Filter service works with optimized queryset: {count} loans")
        
        # Apply date range filter
        filtered_qs = ReportFilterService.apply_date_range_filter(
            filtered_qs,
            start_date=timezone.now().date() - timedelta(days=60),
            end_date=timezone.now().date(),
            date_field='disbursement_date'
        )
        
        count = filtered_qs.count()
        self.assertGreaterEqual(count, 0)
        print(f"✓ Date range filter works: {count} loans")
    
    def test_optimize_for_report_type(self):
        """Test report-type-specific optimization"""
        base_qs = Loan.objects.all()
        
        # Test different report types
        report_types = ['loans_due', 'processing_fees', 'interest_income', 'delinquent']
        
        for report_type in report_types:
            optimized_qs = QueryOptimizer.optimize_for_report_type(base_qs, report_type)
            self.assertIsNotNone(optimized_qs)
            print(f"✓ Optimization for {report_type} report working")


# Run tests
if __name__ == '__main__':
    import django
    from django.core.management import call_command
    django.setup()
    call_command('test', 'reports.test_performance_simple', verbosity=2)
