"""
Standalone property-based test for interest income aggregation.
This bypasses Django's test framework to avoid migration issues.

Feature: reports-system-enhancement, Property 18: Interest income aggregation
Validates: Requirements 7.3
"""

import os
import sys

# Add project root to path
project_root = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, project_root)

import django

# Setup Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
django.setup()

from decimal import Decimal
from django.utils import timezone
from datetime import timedelta
import uuid
from hypothesis import given, strategies as st, settings

from loans.models import Loan, LoanProduct, LoanApplication
from users.models import CustomUser
from django.db.models import Sum


def cleanup_test_data(loans):
    """Clean up test data"""
    for loan in loans:
        if hasattr(loan, 'application') and loan.application:
            loan.application.delete()
        loan.delete()


@settings(max_examples=50, deadline=None)
@given(
    num_loans=st.integers(min_value=1, max_value=20),
    interest_amounts=st.lists(
        st.decimals(
            min_value=Decimal('0.00'),
            max_value=Decimal('50000.00'),
            places=2,
            allow_nan=False,
            allow_infinity=False
        ),
        min_size=1,
        max_size=20
    )
)
def test_interest_income_aggregation_equals_sum(num_loans, interest_amounts):
    """
    Property: The total interest income should equal the sum of all individual
    interest_amount fields from filtered loans.
    """
    # Ensure we have the right number of interest amounts
    if len(interest_amounts) < num_loans:
        interest_amounts = interest_amounts + [Decimal('1000.00')] * (num_loans - len(interest_amounts))
    interest_amounts = interest_amounts[:num_loans]
    
    # Create test user
    user = CustomUser.objects.create_user(
        username=f'test_user_ii_{uuid.uuid4().hex[:8]}',
        email=f'testii_{uuid.uuid4().hex[:8]}@example.com',
        phone_number=f'+2547{uuid.uuid4().hex[:8]}',
        first_name='Test',
        last_name='User'
    )
    
    # Create test loan product
    product = LoanProduct.objects.create(
        name=f'Test Product II {uuid.uuid4().hex[:6]}',
        product_type='boost',
        description='Test product for interest income',
        min_amount=Decimal('1000'),
        max_amount=Decimal('50000'),
        interest_rate=Decimal('10.0'),
        processing_fee=Decimal('5.0'),
        min_duration=7,
        max_duration=90,
        available_repayment_methods=['monthly']
    )
    
    # Create loans with specific interest amounts
    created_loans = []
    expected_total = Decimal('0.00')
    
    try:
        for i, interest in enumerate(interest_amounts):
            # Create loan application
            application = LoanApplication.objects.create(
                borrower=user,
                loan_product=product,
                requested_amount=Decimal('10000.00'),
                loan_purpose='Test purpose',
                status='approved'
            )
            
            # Create loan with specific interest amount
            principal = Decimal('10000.00')
            processing_fee = Decimal('500.00')
            total = principal + interest + processing_fee
            
            loan = Loan.objects.create(
                borrower=user,
                application=application,
                principal_amount=principal,
                interest_amount=interest,
                processing_fee=processing_fee,
                total_amount=total,
                disbursement_date=timezone.now().date(),
                due_date=timezone.now().date() + timedelta(days=30),
                status='active',
                is_deleted=False,
                is_rolled_over=False
            )
            created_loans.append(loan)
            expected_total += interest
        
        # Get all loans (no filtering) - exclude rolled-over and deleted
        queryset = Loan.objects.filter(
            id__in=[loan.id for loan in created_loans],
            is_deleted=False,
            is_rolled_over=False,
            status='active'
        )
        
        # Calculate total interest income using aggregation
        calculated_total = queryset.aggregate(
            total=Sum('interest_amount')
        )['total'] or Decimal('0.00')
        
        # Property: Calculated total should equal expected sum
        assert calculated_total == expected_total, \
            f"Interest income aggregation failed: {calculated_total} != {expected_total}"
        
        # Additional property: Each loan's interest should be non-negative
        for loan in queryset:
            assert loan.interest_amount >= Decimal('0.00'), \
                f"Loan {loan.id} has negative interest amount"
        
        print(f"✓ Test passed: {num_loans} loans, expected={expected_total}, calculated={calculated_total}")
        
    finally:
        # Cleanup
        cleanup_test_data(created_loans)
        product.delete()
        user.delete()


@settings(max_examples=50, deadline=None)
@given(
    num_loans=st.integers(min_value=5, max_value=15),
    filter_ratio=st.floats(min_value=0.1, max_value=0.9)
)
def test_interest_income_aggregation_with_filtering(num_loans, filter_ratio):
    """
    Property: When filtering loans, the aggregated interest should equal
    the sum of interest from only the filtered loans.
    """
    # Create test user
    user = CustomUser.objects.create_user(
        username=f'test_user_iif_{uuid.uuid4().hex[:8]}',
        email=f'testiif_{uuid.uuid4().hex[:8]}@example.com',
        phone_number=f'+2547{uuid.uuid4().hex[:8]}',
        first_name='Test',
        last_name='User'
    )
    
    # Create test loan product
    product = LoanProduct.objects.create(
        name=f'Test Product IIF {uuid.uuid4().hex[:6]}',
        product_type='boost',
        description='Test product for interest income filtering',
        min_amount=Decimal('1000'),
        max_amount=Decimal('50000'),
        interest_rate=Decimal('10.0'),
        processing_fee=Decimal('5.0'),
        min_duration=7,
        max_duration=90,
        available_repayment_methods=['monthly']
    )
    
    # Create loans with varying interest amounts
    created_loans = []
    all_interest = []
    
    try:
        for i in range(num_loans):
            interest = Decimal(str(1000 + (i * 500)))
            all_interest.append(interest)
            
            # Create loan application
            application = LoanApplication.objects.create(
                borrower=user,
                loan_product=product,
                requested_amount=Decimal('10000.00'),
                loan_purpose='Test purpose',
                status='approved'
            )
            
            # Create loan
            loan = Loan.objects.create(
                borrower=user,
                application=application,
                principal_amount=Decimal('10000.00'),
                interest_amount=interest,
                processing_fee=Decimal('500.00'),
                total_amount=Decimal('10000.00') + interest + Decimal('500.00'),
                disbursement_date=timezone.now().date() - timedelta(days=i),
                due_date=timezone.now().date() + timedelta(days=30),
                status='active',
                is_deleted=False,
                is_rolled_over=False
            )
            created_loans.append(loan)
        
        # Filter a subset of loans (by date range)
        filter_count = max(1, int(num_loans * filter_ratio))
        cutoff_date = timezone.now().date() - timedelta(days=filter_count)
        
        filtered_queryset = Loan.objects.filter(
            id__in=[loan.id for loan in created_loans],
            disbursement_date__gte=cutoff_date,
            is_deleted=False,
            is_rolled_over=False,
            status='active'
        )
        
        # Calculate expected total from filtered loans
        expected_filtered_total = sum([
            loan.interest_amount for loan in created_loans
            if loan.disbursement_date >= cutoff_date
        ])
        
        # Calculate total using aggregation
        calculated_filtered_total = filtered_queryset.aggregate(
            total=Sum('interest_amount')
        )['total'] or Decimal('0.00')
        
        # Property: Filtered aggregation should equal sum of filtered loans
        assert calculated_filtered_total == expected_filtered_total, \
            f"Filtered interest aggregation failed: {calculated_filtered_total} != {expected_filtered_total}"
        
        # Property: Filtered total should be <= total of all loans
        total_all = sum(all_interest)
        assert calculated_filtered_total <= total_all, \
            "Filtered total should not exceed total of all loans"
        
        print(f"✓ Filtering test passed: {num_loans} loans, filter_ratio={filter_ratio:.2f}, " +
              f"filtered_total={calculated_filtered_total}")
        
    finally:
        # Cleanup
        cleanup_test_data(created_loans)
        product.delete()
        user.delete()


if __name__ == '__main__':
    print("="*80)
    print("Testing Property 18: Interest Income Aggregation")
    print("="*80)
    
    try:
        print("\n1. Testing basic aggregation property...")
        test_interest_income_aggregation_equals_sum()
        print("✓ Basic aggregation tests passed!")
        
        print("\n2. Testing aggregation with filtering...")
        test_interest_income_aggregation_with_filtering()
        print("✓ Filtering aggregation tests passed!")
        
        print("\n" + "="*80)
        print("ALL TESTS PASSED!")
        print("="*80)
        
    except Exception as e:
        print(f"\n✗ TEST FAILED: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)

