"""
Comprehensive Unit Tests for Analytics and Reporting Services

This module contains unit tests for analytics calculations, reporting functionality,
and data integrity validation.
"""

from django.test import TestCase, TransactionTestCase
from django.utils import timezone
from django.contrib.auth import get_user_model
from unittest.mock import patch, MagicMock, call
from decimal import Decimal
from datetime import date, timedelta
import json

from users.models import Branch
from users.enhanced_permissions_models import PortfolioSnapshot, ClientGrowthMetrics
from users.portfolio_snapshot_service import PortfolioSnapshotService
from users.client_growth_analytics import ClientGrowthAnalytics
from users.growth_forecasting_service import GrowthForecastingService
from reports.enhanced_pdf_service import EnhancedPDFService
from reports.export_functions import ExportService
from reports.advanced_filtering_service import AdvancedFilteringService
from reports.batch_processing_service import BatchProcessingService

User = get_user_model()


class PortfolioAnalyticsTest(TestCase):
    """Test cases for portfolio analytics calculations"""
    
    def setUp(self):
        """Set up test data"""
        self.branch = Branch.objects.create(
            name="Test Branch",
            code="TB001",
            is_active=True
        )
        
        self.loan_officer = User.objects.create_user(
            username="officer1",
            email="officer1@test.com",
            password="testpass123",
            role="loan_officer",
            branch=self.branch,
            phone_number="+254700000002"
        )
        
        self.service = PortfolioSnapshotService()
    
    def test_portfolio_metrics_calculation(self):
        """Test portfolio metrics calculation accuracy"""
        target_date = timezone.now().date()
        
        # Create test snapshots with known values
        snapshot1 = PortfolioSnapshot.objects.create(
            manager=self.loan_officer,
            branch=self.branch,
            snapshot_date=target_date,
            total_clients=10,
            active_clients=9,
            active_loans=8,
            total_disbursed=Decimal('100000.00'),
            total_outstanding=Decimal('60000.00'),
            total_collected=Decimal('40000.00'),
            collection_rate=Decimal('85.00'),
            default_rate=Decimal('5.00'),
            portfolio_yield=Decimal('12.00')
        )
        
        # Test health score calculation
        health_score = snapshot1.get_portfolio_health_score()
        
        # Health score should be calculated correctly
        self.assertIsInstance(health_score, Decimal)
        self.assertGreaterEqual(health_score, 0)
        self.assertLessEqual(health_score, 100)
        
        # Test with good metrics - should have high health score
        self.assertGreater(health_score, 70)
    
    def test_portfolio_health_score_edge_cases(self):
        """Test portfolio health score with edge cases"""
        target_date = timezone.now().date()
        
        # Test with zero values
        snapshot_zero = PortfolioSnapshot.objects.create(
            manager=self.loan_officer,
            branch=self.branch,
            snapshot_date=target_date,
            total_clients=0,
            collection_rate=Decimal('0.00'),
            default_rate=Decimal('0.00'),
            portfolio_yield=Decimal('0.00')
        )
        
        health_score = snapshot_zero.get_portfolio_health_score()
        self.assertGreaterEqual(health_score, 0)
        
        # Test with poor metrics
        snapshot_poor = PortfolioSnapshot.objects.create(
            manager=self.loan_officer,
            branch=self.branch,
            snapshot_date=target_date - timedelta(days=1),
            total_clients=10,
            collection_rate=Decimal('50.00'),  # Poor collection
            default_rate=Decimal('20.00'),     # High default rate
            portfolio_yield=Decimal('5.00')    # Low yield
        )
        
        health_score_poor = snapshot_poor.get_portfolio_health_score()
        self.assertLess(health_score_poor, 50)  # Should be low
    
    def test_growth_rate_calculation(self):
        """Test growth rate calculation accuracy"""
        today = timezone.now().date()
        past_date = today - timedelta(days=30)
        
        # Create snapshots for growth calculation
        snapshot_past = PortfolioSnapshot.objects.create(
            manager=self.loan_officer,
            branch=self.branch,
            snapshot_date=past_date,
            total_clients=8,
            active_loans=6,
            total_disbursed=Decimal('80000.00')
        )
        
        snapshot_current = PortfolioSnapshot.objects.create(
            manager=self.loan_officer,
            branch=self.branch,
            snapshot_date=today,
            total_clients=10,
            active_loans=8,
            total_disbursed=Decimal('100000.00')
        )
        
        # Test client growth rate
        client_growth = snapshot_current.calculate_growth_rate(snapshot_past, 'total_clients')
        self.assertEqual(client_growth, Decimal('25.00'))  # (10-8)/8 * 100
        
        # Test loan growth rate
        loan_growth = snapshot_current.calculate_growth_rate(snapshot_past, 'active_loans')
        self.assertEqual(loan_growth, Decimal('33.33'))  # (8-6)/6 * 100
        
        # Test disbursement growth rate
        disbursement_growth = snapshot_current.calculate_growth_rate(snapshot_past, 'total_disbursed')
        self.assertEqual(disbursement_growth, Decimal('25.00'))  # (100000-80000)/80000 * 100
    
    def test_portfolio_trends_analysis(self):
        """Test portfolio trends analysis"""
        base_date = timezone.now().date()
        
        # Create multiple snapshots over time
        for i in range(7):  # 7 days of data
            snapshot_date = base_date - timedelta(days=i)
            PortfolioSnapshot.objects.create(
                manager=self.loan_officer,
                branch=self.branch,
                snapshot_date=snapshot_date,
                total_clients=10 + i,
                active_loans=8 + i,
                total_disbursed=Decimal(str(50000 + (i * 5000))),
                collection_rate=Decimal('85.00') + Decimal(str(i)),
                default_rate=Decimal('5.00') - Decimal(str(i * 0.5))
            )
        
        # Get trends
        trends = self.service.get_portfolio_trends(self.loan_officer, 7)
        
        # Verify trends structure
        self.assertIn('period', trends)
        self.assertIn('client_growth', trends)
        self.assertIn('loan_growth', trends)
        self.assertIn('daily_data', trends)
        self.assertEqual(len(trends['daily_data']), 7)
        
        # Verify trend calculations
        self.assertIsInstance(trends['client_growth'], (int, float, Decimal))
        self.assertIsInstance(trends['loan_growth'], (int, float, Decimal))
    
    def test_performance_alerts_generation(self):
        """Test performance alerts generation"""
        target_date = timezone.now().date()
        
        # Create snapshot with poor performance
        snapshot = PortfolioSnapshot.objects.create(
            manager=self.loan_officer,
            branch=self.branch,
            snapshot_date=target_date,
            total_clients=10,
            collection_rate=Decimal('60.00'),  # Poor collection rate
            default_rate=Decimal('15.00'),     # High default rate
            total_outstanding=Decimal('50000.00'),
            par_30=Decimal('10000.00')         # High PAR
        )
        
        health_score = Decimal('35.00')  # Critical health score
        
        # Mock notification creation
        with patch('users.portfolio_snapshot_service.Notification') as mock_notification:
            with patch.object(self.service, '_send_alert_email') as mock_email:
                self.service._check_performance_alerts(self.loan_officer, snapshot, health_score)
                
                # Verify alerts were generated
                self.assertTrue(mock_notification.objects.create.called)
                self.assertTrue(mock_email.called)
    
    def test_portfolio_comparison_metrics(self):
        """Test portfolio comparison between managers"""
        # Create another loan officer
        officer2 = User.objects.create_user(
            username="officer2",
            email="officer2@test.com",
            password="testpass123",
            role="loan_officer",
            branch=self.branch,
            phone_number="+254700000003"
        )
        
        target_date = timezone.now().date()
        
        # Create snapshots for both officers
        snapshot1 = PortfolioSnapshot.objects.create(
            manager=self.loan_officer,
            branch=self.branch,
            snapshot_date=target_date,
            total_clients=10,
            collection_rate=Decimal('85.00'),
            default_rate=Decimal('5.00')
        )
        
        snapshot2 = PortfolioSnapshot.objects.create(
            manager=officer2,
            branch=self.branch,
            snapshot_date=target_date,
            total_clients=15,
            collection_rate=Decimal('90.00'),
            default_rate=Decimal('3.00')
        )
        
        # Get comparison metrics
        comparison = self.service.get_portfolio_comparison([self.loan_officer, officer2], target_date)
        
        # Verify comparison structure
        self.assertIn('managers', comparison)
        self.assertIn('metrics', comparison)
        self.assertEqual(len(comparison['managers']), 2)
        
        # Verify ranking
        self.assertIn('ranking', comparison)
        self.assertIsInstance(comparison['ranking'], list)


class ClientGrowthAnalyticsTest(TestCase):
    """Test cases for client growth analytics"""
    
    def setUp(self):
        """Set up test data"""
        self.branch = Branch.objects.create(
            name="Test Branch",
            code="TB001",
            is_active=True
        )
        
        self.loan_officer = User.objects.create_user(
            username="officer1",
            email="officer1@test.com",
            password="testpass123",
            role="loan_officer",
            branch=self.branch,
            phone_number="+254700000002"
        )
        
        self.analytics_service = ClientGrowthAnalytics()
    
    def test_client_acquisition_trends(self):
        """Test client acquisition trends calculation"""
        # Create test client growth metrics
        base_date = timezone.now().date()
        
        for i in range(6):  # 6 months of data
            month_start = base_date.replace(day=1) - timedelta(days=30*i)
            month_end = month_start + timedelta(days=29)
            
            ClientGrowthMetrics.objects.create(
                branch=self.branch,
                period_start=month_start,
                period_end=month_end,
                new_clients=10 + i,
                churned_clients=2,
                reactivated_clients=1,
                total_clients=50 + (i * 8),
                acquisition_cost=Decimal('500.00'),
                lifetime_value=Decimal('2000.00')
            )
        
        # Get acquisition trends
        trends = self.analytics_service.get_acquisition_trends(self.branch.id, 6)
        
        # Verify trends structure
        self.assertIn('period_months', trends)
        self.assertIn('monthly_data', trends)
        self.assertIn('total_acquired', trends)
        self.assertIn('average_monthly', trends)
        self.assertEqual(len(trends['monthly_data']), 6)
    
    def test_demographic_analysis(self):
        """Test client demographic analysis"""
        # Mock client data for demographic analysis
        with patch.object(self.analytics_service, '_get_client_demographics') as mock_demographics:
            mock_demographics.return_value = {
                'age_groups': {
                    '18-25': 20,
                    '26-35': 35,
                    '36-45': 30,
                    '46+': 15
                },
                'gender': {
                    'male': 60,
                    'female': 40
                },
                'income_brackets': {
                    'low': 25,
                    'medium': 50,
                    'high': 25
                }
            }
            
            # Get demographic analysis
            demographics = self.analytics_service.get_demographic_analysis({'branch_id': self.branch.id})
            
            # Verify demographic structure
            self.assertIn('age_groups', demographics)
            self.assertIn('gender', demographics)
            self.assertIn('income_brackets', demographics)
            
            # Verify percentages add up correctly
            age_total = sum(demographics['age_groups'].values())
            self.assertEqual(age_total, 100)
    
    def test_lifecycle_analytics(self):
        """Test client lifecycle analytics"""
        # Mock lifecycle data
        with patch.object(self.analytics_service, '_calculate_lifecycle_metrics') as mock_lifecycle:
            mock_lifecycle.return_value = {
                'average_lifespan_days': 365,
                'stages': {
                    'new': {'count': 20, 'avg_duration_days': 30},
                    'active': {'count': 60, 'avg_duration_days': 300},
                    'dormant': {'count': 15, 'avg_duration_days': 90},
                    'churned': {'count': 5, 'avg_duration_days': 0}
                },
                'conversion_rates': {
                    'new_to_active': 85.0,
                    'active_to_dormant': 20.0,
                    'dormant_to_churned': 30.0
                }
            }
            
            # Get lifecycle analytics
            lifecycle = self.analytics_service.get_lifecycle_analytics('active_clients')
            
            # Verify lifecycle structure
            self.assertIn('average_lifespan_days', lifecycle)
            self.assertIn('stages', lifecycle)
            self.assertIn('conversion_rates', lifecycle)
            
            # Verify conversion rates are percentages
            for rate in lifecycle['conversion_rates'].values():
                self.assertGreaterEqual(rate, 0)
                self.assertLessEqual(rate, 100)
    
    def test_officer_performance_metrics(self):
        """Test loan officer performance metrics"""
        # Create test data for officer performance
        target_date = timezone.now().date()
        
        # Mock performance data
        with patch.object(self.analytics_service, '_calculate_officer_metrics') as mock_metrics:
            mock_metrics.return_value = {
                'client_acquisition': {
                    'new_clients': 15,
                    'acquisition_rate': 12.5,
                    'conversion_rate': 85.0
                },
                'portfolio_performance': {
                    'total_clients': 120,
                    'active_clients': 110,
                    'retention_rate': 91.7
                },
                'loan_performance': {
                    'loans_disbursed': 25,
                    'approval_rate': 78.0,
                    'default_rate': 4.2
                },
                'collection_performance': {
                    'collection_rate': 88.5,
                    'recovery_rate': 92.0
                }
            }
            
            # Get officer performance
            performance = self.analytics_service.get_officer_performance(self.loan_officer.id, 30)
            
            # Verify performance structure
            self.assertIn('client_acquisition', performance)
            self.assertIn('portfolio_performance', performance)
            self.assertIn('loan_performance', performance)
            self.assertIn('collection_performance', performance)
            
            # Verify metrics are reasonable
            self.assertGreaterEqual(performance['portfolio_performance']['retention_rate'], 0)
            self.assertLessEqual(performance['portfolio_performance']['retention_rate'], 100)
    
    def test_growth_forecasting(self):
        """Test growth forecasting calculations"""
        # Create historical data for forecasting
        base_date = timezone.now().date()
        
        for i in range(12):  # 12 months of historical data
            month_start = base_date.replace(day=1) - timedelta(days=30*i)
            month_end = month_start + timedelta(days=29)
            
            ClientGrowthMetrics.objects.create(
                branch=self.branch,
                period_start=month_start,
                period_end=month_end,
                new_clients=10 + (i % 3),  # Seasonal pattern
                total_clients=100 + (i * 5),
                acquisition_cost=Decimal('500.00'),
                lifetime_value=Decimal('2000.00')
            )
        
        forecasting_service = GrowthForecastingService()
        
        # Get growth forecast
        forecast = forecasting_service.generate_growth_forecast(self.branch.id, 6)  # 6 months ahead
        
        # Verify forecast structure
        self.assertIn('forecast_period', forecast)
        self.assertIn('predicted_growth', forecast)
        self.assertIn('confidence_interval', forecast)
        self.assertIn('seasonal_factors', forecast)
        
        # Verify forecast data
        self.assertEqual(forecast['forecast_period'], 6)
        self.assertIsInstance(forecast['predicted_growth'], list)
        self.assertEqual(len(forecast['predicted_growth']), 6)
    
    def test_capacity_planning(self):
        """Test capacity planning calculations"""
        forecasting_service = GrowthForecastingService()
        
        # Mock current capacity data
        current_capacity = {
            'loan_officers': 5,
            'max_clients_per_officer': 50,
            'current_utilization': 0.8
        }
        
        # Mock growth projection
        growth_projection = {
            'new_clients_per_month': 15,
            'forecast_months': 12
        }
        
        # Calculate capacity requirements
        capacity_plan = forecasting_service.calculate_capacity_requirements(
            current_capacity, growth_projection
        )
        
        # Verify capacity plan structure
        self.assertIn('current_capacity', capacity_plan)
        self.assertIn('projected_demand', capacity_plan)
        self.assertIn('capacity_gap', capacity_plan)
        self.assertIn('recommendations', capacity_plan)
        
        # Verify calculations
        projected_clients = growth_projection['new_clients_per_month'] * growth_projection['forecast_months']
        self.assertGreater(capacity_plan['projected_demand'], 0)


class ReportGenerationTest(TestCase):
    """Test cases for report generation and export functionality"""
    
    def setUp(self):
        """Set up test data"""
        self.branch = Branch.objects.create(
            name="Test Branch",
            code="TB001",
            is_active=True
        )
        
        self.user = User.objects.create_user(
            username="testuser",
            email="test@test.com",
            password="testpass123",
            role="loan_officer",
            branch=self.branch,
            phone_number="+254700000001"
        )
        
        self.pdf_service = EnhancedPDFService()
        self.export_service = ExportService()
    
    def test_pdf_generation_with_branding(self):
        """Test PDF generation with company branding"""
        # Mock report data
        report_data = {
            'title': 'Portfolio Performance Report',
            'date_range': '2024-01-01 to 2024-01-31',
            'manager': self.user.get_full_name(),
            'metrics': {
                'total_clients': 50,
                'active_loans': 45,
                'collection_rate': 85.5
            },
            'charts': []
        }
        
        # Generate PDF
        pdf_result = self.pdf_service.generate_portfolio_report(report_data)
        
        # Verify PDF generation
        self.assertIn('success', pdf_result)
        self.assertTrue(pdf_result['success'])
        self.assertIn('file_path', pdf_result)
        self.assertIn('file_size', pdf_result)
    
    def test_excel_export_with_formatting(self):
        """Test Excel export with proper formatting"""
        # Mock export data
        export_data = [
            {
                'client_name': 'John Doe',
                'loan_amount': Decimal('10000.00'),
                'status': 'Active',
                'due_date': timezone.now().date()
            },
            {
                'client_name': 'Jane Smith',
                'loan_amount': Decimal('15000.00'),
                'status': 'Completed',
                'due_date': timezone.now().date() - timedelta(days=30)
            }
        ]
        
        # Export to Excel
        excel_result = self.export_service.export_to_excel(
            data=export_data,
            filename='test_export',
            sheet_name='Loan Data',
            formatting_rules={
                'loan_amount': 'currency',
                'due_date': 'date'
            }
        )
        
        # Verify Excel export
        self.assertIn('success', excel_result)
        self.assertTrue(excel_result['success'])
        self.assertIn('file_path', excel_result)
        self.assertIn('rows_exported', excel_result)
        self.assertEqual(excel_result['rows_exported'], 2)
    
    def test_csv_export_functionality(self):
        """Test CSV export functionality"""
        # Mock export data
        export_data = [
            {'name': 'Client 1', 'amount': 1000, 'status': 'Active'},
            {'name': 'Client 2', 'amount': 2000, 'status': 'Pending'}
        ]
        
        # Export to CSV
        csv_result = self.export_service.export_to_csv(
            data=export_data,
            filename='test_export.csv'
        )
        
        # Verify CSV export
        self.assertIn('success', csv_result)
        self.assertTrue(csv_result['success'])
        self.assertIn('file_path', csv_result)
        self.assertIn('rows_exported', csv_result)
    
    def test_batch_export_processing(self):
        """Test batch export processing"""
        batch_service = BatchProcessingService()
        
        # Mock batch export request
        export_requests = [
            {
                'type': 'pdf',
                'template': 'portfolio_report',
                'data': {'manager_id': self.user.id}
            },
            {
                'type': 'excel',
                'template': 'client_list',
                'data': {'branch_id': self.branch.id}
            }
        ]
        
        # Process batch exports
        with patch.object(batch_service, '_process_single_export') as mock_process:
            mock_process.return_value = {'success': True, 'file_path': '/tmp/test.pdf'}
            
            batch_result = batch_service.process_batch_exports(export_requests)
            
            # Verify batch processing
            self.assertIn('success', batch_result)
            self.assertTrue(batch_result['success'])
            self.assertIn('completed', batch_result)
            self.assertIn('failed', batch_result)
            self.assertEqual(batch_result['completed'], 2)
    
    def test_report_scheduling(self):
        """Test report scheduling functionality"""
        batch_service = BatchProcessingService()
        
        # Mock scheduled report
        schedule_config = {
            'report_type': 'portfolio_summary',
            'frequency': 'weekly',
            'recipients': ['manager@test.com'],
            'parameters': {
                'branch_id': self.branch.id,
                'include_charts': True
            }
        }
        
        # Schedule report
        schedule_result = batch_service.schedule_report(schedule_config)
        
        # Verify scheduling
        self.assertIn('success', schedule_result)
        self.assertTrue(schedule_result['success'])
        self.assertIn('schedule_id', schedule_result)
        self.assertIn('next_run', schedule_result)


class AdvancedFilteringTest(TestCase):
    """Test cases for advanced filtering and sorting"""
    
    def setUp(self):
        """Set up test data"""
        self.filtering_service = AdvancedFilteringService()
        
        # Mock data for filtering
        self.test_data = [
            {
                'id': 1,
                'name': 'John Doe',
                'amount': Decimal('10000.00'),
                'status': 'Active',
                'date': timezone.now().date(),
                'branch': 'Main'
            },
            {
                'id': 2,
                'name': 'Jane Smith',
                'amount': Decimal('15000.00'),
                'status': 'Pending',
                'date': timezone.now().date() - timedelta(days=10),
                'branch': 'Branch A'
            },
            {
                'id': 3,
                'name': 'Bob Johnson',
                'amount': Decimal('5000.00'),
                'status': 'Active',
                'date': timezone.now().date() - timedelta(days=5),
                'branch': 'Main'
            }
        ]
    
    def test_basic_filtering(self):
        """Test basic filtering functionality"""
        # Filter by status
        filters = {'status': 'Active'}
        filtered_data = self.filtering_service.apply_filters(self.test_data, filters)
        
        # Verify filtering
        self.assertEqual(len(filtered_data), 2)
        for item in filtered_data:
            self.assertEqual(item['status'], 'Active')
    
    def test_range_filtering(self):
        """Test range filtering for amounts and dates"""
        # Filter by amount range
        filters = {
            'amount_min': Decimal('8000.00'),
            'amount_max': Decimal('12000.00')
        }
        
        filtered_data = self.filtering_service.apply_filters(self.test_data, filters)
        
        # Verify range filtering
        self.assertEqual(len(filtered_data), 1)
        self.assertEqual(filtered_data[0]['name'], 'John Doe')
    
    def test_date_range_filtering(self):
        """Test date range filtering"""
        # Filter by date range (last 7 days)
        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=7)
        
        filters = {
            'date_start': start_date,
            'date_end': end_date
        }
        
        filtered_data = self.filtering_service.apply_filters(self.test_data, filters)
        
        # Verify date filtering
        for item in filtered_data:
            self.assertGreaterEqual(item['date'], start_date)
            self.assertLessEqual(item['date'], end_date)
    
    def test_multiple_filters(self):
        """Test applying multiple filters simultaneously"""
        filters = {
            'status': 'Active',
            'branch': 'Main',
            'amount_min': Decimal('8000.00')
        }
        
        filtered_data = self.filtering_service.apply_filters(self.test_data, filters)
        
        # Verify multiple filters
        self.assertEqual(len(filtered_data), 1)
        item = filtered_data[0]
        self.assertEqual(item['status'], 'Active')
        self.assertEqual(item['branch'], 'Main')
        self.assertGreaterEqual(item['amount'], Decimal('8000.00'))
    
    def test_sorting_functionality(self):
        """Test sorting functionality"""
        # Sort by amount descending
        sort_rules = [{'field': 'amount', 'direction': 'desc'}]
        sorted_data = self.filtering_service.apply_sorting(self.test_data, sort_rules)
        
        # Verify sorting
        self.assertEqual(sorted_data[0]['amount'], Decimal('15000.00'))
        self.assertEqual(sorted_data[1]['amount'], Decimal('10000.00'))
        self.assertEqual(sorted_data[2]['amount'], Decimal('5000.00'))
    
    def test_multi_column_sorting(self):
        """Test multi-column sorting"""
        # Sort by branch, then by amount
        sort_rules = [
            {'field': 'branch', 'direction': 'asc'},
            {'field': 'amount', 'direction': 'desc'}
        ]
        
        sorted_data = self.filtering_service.apply_sorting(self.test_data, sort_rules)
        
        # Verify multi-column sorting
        # Branch A items first, then Main branch items
        self.assertEqual(sorted_data[0]['branch'], 'Branch A')
        self.assertEqual(sorted_data[1]['branch'], 'Main')
        self.assertEqual(sorted_data[2]['branch'], 'Main')
        
        # Within Main branch, higher amount first
        main_items = [item for item in sorted_data if item['branch'] == 'Main']
        self.assertGreater(main_items[0]['amount'], main_items[1]['amount'])
    
    def test_search_functionality(self):
        """Test search functionality"""
        # Search by name
        search_term = 'John'
        search_fields = ['name']
        
        search_results = self.filtering_service.apply_search(
            self.test_data, search_term, search_fields
        )
        
        # Verify search results
        self.assertEqual(len(search_results), 2)  # John Doe and Bob Johnson
        for item in search_results:
            self.assertIn('John', item['name'])
    
    def test_filter_persistence(self):
        """Test filter persistence functionality"""
        # Save filter preset
        filter_preset = {
            'name': 'Active High Value',
            'filters': {
                'status': 'Active',
                'amount_min': Decimal('8000.00')
            },
            'sort': [{'field': 'amount', 'direction': 'desc'}]
        }
        
        # Save preset
        preset_id = self.filtering_service.save_filter_preset(filter_preset)
        self.assertIsNotNone(preset_id)
        
        # Load preset
        loaded_preset = self.filtering_service.load_filter_preset(preset_id)
        self.assertEqual(loaded_preset['name'], 'Active High Value')
        self.assertEqual(loaded_preset['filters']['status'], 'Active')


class DataIntegrityTest(TestCase):
    """Test cases for data integrity validation"""
    
    def setUp(self):
        """Set up test data"""
        self.branch = Branch.objects.create(
            name="Test Branch",
            code="TB001",
            is_active=True
        )
        
        self.user = User.objects.create_user(
            username="testuser",
            email="test@test.com",
            password="testpass123",
            role="loan_officer",
            branch=self.branch,
            phone_number="+254700000001"
        )
    
    def test_portfolio_snapshot_data_integrity(self):
        """Test portfolio snapshot data integrity"""
        # Create snapshot with valid data
        snapshot = PortfolioSnapshot.objects.create(
            manager=self.user,
            branch=self.branch,
            snapshot_date=timezone.now().date(),
            total_clients=10,
            active_clients=9,
            active_loans=8,
            total_disbursed=Decimal('100000.00'),
            total_outstanding=Decimal('60000.00'),
            total_collected=Decimal('40000.00')
        )
        
        # Validate data integrity
        self.assertLessEqual(snapshot.active_clients, snapshot.total_clients)
        self.assertLessEqual(snapshot.active_loans, snapshot.active_clients)
        self.assertEqual(
            snapshot.total_disbursed,
            snapshot.total_outstanding + snapshot.total_collected
        )
    
    def test_client_growth_metrics_integrity(self):
        """Test client growth metrics data integrity"""
        # Create growth metrics
        metrics = ClientGrowthMetrics.objects.create(
            branch=self.branch,
            period_start=timezone.now().date().replace(day=1),
            period_end=timezone.now().date(),
            new_clients=10,
            churned_clients=2,
            reactivated_clients=1,
            total_clients=50,
            acquisition_cost=Decimal('500.00'),
            lifetime_value=Decimal('2000.00')
        )
        
        # Validate data integrity
        self.assertGreaterEqual(metrics.new_clients, 0)
        self.assertGreaterEqual(metrics.churned_clients, 0)
        self.assertGreaterEqual(metrics.total_clients, 0)
        self.assertGreater(metrics.lifetime_value, metrics.acquisition_cost)
    
    def test_calculation_accuracy(self):
        """Test accuracy of calculations"""
        # Test percentage calculations
        total = 100
        part = 25
        percentage = (part / total) * 100
        
        self.assertEqual(percentage, 25.0)
        
        # Test growth rate calculations
        old_value = Decimal('80.00')
        new_value = Decimal('100.00')
        growth_rate = ((new_value - old_value) / old_value) * 100
        
        self.assertEqual(growth_rate, Decimal('25.00'))
    
    def test_decimal_precision(self):
        """Test decimal precision in calculations"""
        # Test monetary calculations
        amount1 = Decimal('10000.50')
        amount2 = Decimal('5000.25')
        total = amount1 + amount2
        
        self.assertEqual(total, Decimal('15000.75'))
        
        # Test percentage calculations with decimals
        rate = Decimal('12.5')
        amount = Decimal('1000.00')
        result = (amount * rate) / 100
        
        self.assertEqual(result, Decimal('125.00'))
    
    def test_date_range_validation(self):
        """Test date range validation"""
        start_date = timezone.now().date()
        end_date = start_date + timedelta(days=30)
        
        # Valid date range
        self.assertLess(start_date, end_date)
        
        # Invalid date range should be caught
        invalid_end = start_date - timedelta(days=1)
        self.assertGreater(start_date, invalid_end)
    
    def test_export_data_integrity(self):
        """Test data integrity in exports"""
        # Mock export data
        original_data = [
            {'id': 1, 'amount': Decimal('1000.50'), 'date': timezone.now().date()},
            {'id': 2, 'amount': Decimal('2000.75'), 'date': timezone.now().date()}
        ]
        
        # Simulate export process
        export_service = ExportService()
        
        # Mock the export process
        with patch.object(export_service, 'export_to_excel') as mock_export:
            mock_export.return_value = {
                'success': True,
                'rows_exported': len(original_data),
                'data_checksum': 'abc123'
            }
            
            result = export_service.export_to_excel(original_data, 'test.xlsx')
            
            # Verify data integrity
            self.assertTrue(result['success'])
            self.assertEqual(result['rows_exported'], 2)
            self.assertIn('data_checksum', result)