"""
Simple test script for Task 7 property tests.
This script tests the three properties without full Django test infrastructure.
"""

import os
import django
import sys

# Setup Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
django.setup()

from datetime import date, datetime, timedelta
from decimal import Decimal
from django.utils import timezone
from loans.models import Loan, LoanProduct, LoanApplication
from users.models import CustomUser
from reports.filter_service import ReportFilterService
import uuid

def cleanup():
    """Clean up test data"""
    Loan.objects.filter(loan_number__startswith='LOAN-TEST-').delete()
    LoanApplication.objects.filter(application_number__startswith='APP-TEST-').delete()

def test_daily_filter_precision():
    """
    Test Property 23: Daily filter precision
    Validates: Requirements 9.2
    """
    print("\n" + "="*80)
    print("Testing Property 23: Daily filter precision")
    print("="*80)
    
    try:
        # Get or create test user
        try:
            user = CustomUser.objects.get(username='testuser_daily_prop')
        except CustomUser.DoesNotExist:
            user = CustomUser.objects.create_user(
                username='testuser_daily_prop',
                email='test_daily_prop@example.com',
                phone_number=f'+2547000{uuid.uuid4().hex[:5]}',
                first_name='Test',
                last_name='User'
            )
        
        # Get or create test product
        product, _ = LoanProduct.objects.get_or_create(
            name='Test Product Daily Prop',
            defaults={
                'product_type': 'boost',
                'description': 'Test product',
                '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': ['daily']
            }
        )
        
        today = timezone.now().date()
        
        # Create 3 loans due today
        loans_due_today = []
        for i in range(3):
            app = LoanApplication.objects.create(
                application_number=f'APP-TEST-TODAY-{uuid.uuid4().hex[:6]}',
                borrower=user,
                loan_product=product,
                requested_amount=Decimal('10000'),
                requested_duration=30,
                purpose='Test',
                status='approved'
            )
            
            loan = Loan.objects.create(
                loan_number=f'LOAN-TEST-TODAY-{uuid.uuid4().hex[:6]}',
                application=app,
                borrower=user,
                principal_amount=Decimal('10000'),
                interest_amount=Decimal('1000'),
                processing_fee=Decimal('500'),
                total_amount=Decimal('11500'),
                disbursement_date=timezone.now() - timedelta(days=15),
                due_date=datetime.combine(today, datetime.min.time()),
                duration_days=30,
                status='active',
                is_deleted=False,
                is_rolled_over=False
            )
            loans_due_today.append(loan)
        
        # Create 3 loans due on other days
        loans_due_other_days = []
        for i in range(3):
            due_date = today + timedelta(days=i + 1)
            
            app = LoanApplication.objects.create(
                application_number=f'APP-TEST-OTHER-{uuid.uuid4().hex[:6]}',
                borrower=user,
                loan_product=product,
                requested_amount=Decimal('10000'),
                requested_duration=30,
                purpose='Test',
                status='approved'
            )
            
            loan = Loan.objects.create(
                loan_number=f'LOAN-TEST-OTHER-{uuid.uuid4().hex[:6]}',
                application=app,
                borrower=user,
                principal_amount=Decimal('10000'),
                interest_amount=Decimal('1000'),
                processing_fee=Decimal('500'),
                total_amount=Decimal('11500'),
                disbursement_date=timezone.now() - timedelta(days=20),
                due_date=datetime.combine(due_date, datetime.min.time()),
                duration_days=30,
                status='active',
                is_deleted=False,
                is_rolled_over=False
            )
            loans_due_other_days.append(loan)
        
        # Apply daily filter
        queryset = Loan.objects.filter(loan_number__startswith='LOAN-TEST-', status='active')
        queryset = ReportFilterService.apply_loan_status_filter(
            queryset, exclude_rolled_over=True, exclude_deleted=True
        )
        queryset = queryset.filter(due_date__date=today)
        
        # Verify all returned loans have due_date = today
        print(f"✓ Found {queryset.count()} loans due today")
        for loan in queryset:
            loan_due_date = loan.due_date.date() if hasattr(loan.due_date, 'date') else loan.due_date
            assert loan_due_date == today, f"FAIL: Loan {loan.loan_number} has due_date {loan_due_date} != today {today}"
            print(f"  ✓ Loan {loan.loan_number} due on {loan_due_date} (correct)")
        
        # Verify no loans due on other days are included
        filtered_ids = set(queryset.values_list('id', flat=True))
        for loan in loans_due_other_days:
            assert loan.id not in filtered_ids, f"FAIL: Loan {loan.loan_number} due on other day was incorrectly included"
        
        # Verify all loans due today are included
        for loan in loans_due_today:
            assert loan.id in filtered_ids, f"FAIL: Loan {loan.loan_number} due today was incorrectly excluded"
        
        print("✓ Property 23 PASSED: Daily filter precision works correctly")
        return True
        
    except Exception as e:
        print(f"✗ Property 23 FAILED: {str(e)}")
        import traceback
        traceback.print_exc()
        return False
    finally:
        cleanup()


def test_weekly_filter_range():
    """
    Test Property 24: Weekly filter range
    Validates: Requirements 9.3
    """
    print("\n" + "="*80)
    print("Testing Property 24: Weekly filter range")
    print("="*80)
    
    try:
        # Get or create test user
        try:
            user = CustomUser.objects.get(username='testuser_weekly_prop')
        except CustomUser.DoesNotExist:
            user = CustomUser.objects.create_user(
                username='testuser_weekly_prop',
                email='test_weekly_prop@example.com',
                phone_number=f'+2547001{uuid.uuid4().hex[:5]}',
                first_name='Test',
                last_name='User'
            )
        
        # Get or create test product
        product, _ = LoanProduct.objects.get_or_create(
            name='Test Product Weekly Prop',
            defaults={
                'product_type': 'boost',
                'description': 'Test product',
                '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': ['weekly']
            }
        )
        
        today = timezone.now().date()
        week_end = today + timedelta(days=7)
        
        # Create 5 loans due within the next 7 days
        loans_within_week = []
        for i in range(5):
            due_date = today + timedelta(days=i)
            
            app = LoanApplication.objects.create(
                application_number=f'APP-TEST-WEEK-{uuid.uuid4().hex[:6]}',
                borrower=user,
                loan_product=product,
                requested_amount=Decimal('10000'),
                requested_duration=30,
                purpose='Test',
                status='approved'
            )
            
            loan = Loan.objects.create(
                loan_number=f'LOAN-TEST-WEEK-{uuid.uuid4().hex[:6]}',
                application=app,
                borrower=user,
                principal_amount=Decimal('10000'),
                interest_amount=Decimal('1000'),
                processing_fee=Decimal('500'),
                total_amount=Decimal('11500'),
                disbursement_date=timezone.now() - timedelta(days=15),
                due_date=datetime.combine(due_date, datetime.min.time()),
                duration_days=30,
                status='active',
                is_deleted=False,
                is_rolled_over=False
            )
            loans_within_week.append(loan)
        
        # Create 3 loans due outside the next 7 days
        loans_outside_week = []
        for i in range(3):
            due_date = today + timedelta(days=8 + i)
            
            app = LoanApplication.objects.create(
                application_number=f'APP-TEST-NOWEEK-{uuid.uuid4().hex[:6]}',
                borrower=user,
                loan_product=product,
                requested_amount=Decimal('10000'),
                requested_duration=30,
                purpose='Test',
                status='approved'
            )
            
            loan = Loan.objects.create(
                loan_number=f'LOAN-TEST-NOWEEK-{uuid.uuid4().hex[:6]}',
                application=app,
                borrower=user,
                principal_amount=Decimal('10000'),
                interest_amount=Decimal('1000'),
                processing_fee=Decimal('500'),
                total_amount=Decimal('11500'),
                disbursement_date=timezone.now() - timedelta(days=20),
                due_date=datetime.combine(due_date, datetime.min.time()),
                duration_days=30,
                status='active',
                is_deleted=False,
                is_rolled_over=False
            )
            loans_outside_week.append(loan)
        
        # Apply weekly filter
        queryset = Loan.objects.filter(loan_number__startswith='LOAN-TEST-', status='active')
        queryset = ReportFilterService.apply_loan_status_filter(
            queryset, exclude_rolled_over=True, exclude_deleted=True
        )
        queryset = queryset.filter(
            due_date__date__gte=today,
            due_date__date__lte=week_end
        )
        
        # Verify all returned loans have due_date within 7 days
        print(f"✓ Found {queryset.count()} loans due within next 7 days")
        for loan in queryset:
            loan_due_date = loan.due_date.date() if hasattr(loan.due_date, 'date') else loan.due_date
            assert today <= loan_due_date <= week_end, \
                f"FAIL: Loan {loan.loan_number} has due_date {loan_due_date} outside range [{today}, {week_end}]"
            print(f"  ✓ Loan {loan.loan_number} due on {loan_due_date} (within range)")
        
        # Verify no loans outside the week are included
        filtered_ids = set(queryset.values_list('id', flat=True))
        for loan in loans_outside_week:
            assert loan.id not in filtered_ids, f"FAIL: Loan {loan.loan_number} due outside week was incorrectly included"
        
        # Verify all loans within the week are included
        for loan in loans_within_week:
            assert loan.id in filtered_ids, f"FAIL: Loan {loan.loan_number} due within week was incorrectly excluded"
        
        print("✓ Property 24 PASSED: Weekly filter range works correctly")
        return True
        
    except Exception as e:
        print(f"✗ Property 24 FAILED: {str(e)}")
        import traceback
        traceback.print_exc()
        return False
    finally:
        cleanup()


def test_filter_completeness():
    """
    Test Property 25: Filter completeness (no false negatives)
    Validates: Requirements 9.5
    """
    print("\n" + "="*80)
    print("Testing Property 25: Filter completeness (no false negatives)")
    print("="*80)
    
    try:
        # Get or create test user
        try:
            user = CustomUser.objects.get(username='testuser_complete_prop')
        except CustomUser.DoesNotExist:
            user = CustomUser.objects.create_user(
                username='testuser_complete_prop',
                email='test_complete_prop@example.com',
                phone_number=f'+2547002{uuid.uuid4().hex[:5]}',
                first_name='Test',
                last_name='User'
            )
        
        # Get or create test product
        product, _ = LoanProduct.objects.get_or_create(
            name='Test Product Complete Prop',
            defaults={
                'product_type': 'boost',
                'description': 'Test product',
                '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']
            }
        )
        
        start_date = date(2024, 1, 1)
        end_date = date(2024, 1, 31)
        
        # Create 5 loans that match ALL filter criteria
        matching_loans = []
        for i in range(5):
            loan_date = start_date + timedelta(days=i * 5)
            
            app = LoanApplication.objects.create(
                application_number=f'APP-T7-{i}',
                borrower=user,
                loan_product=product,
                requested_amount=Decimal('10000'),
                requested_duration=30,
                purpose='Test',
                status='approved'
            )
            
            loan = Loan.objects.create(
                loan_number=f'LOAN-TEST-COMPLETE-{uuid.uuid4().hex[:6]}',
                application=app,
                borrower=user,
                principal_amount=Decimal('10000'),
                interest_amount=Decimal('1000'),
                processing_fee=Decimal('500'),
                total_amount=Decimal('11500'),
                disbursement_date=datetime.combine(loan_date, datetime.min.time()),
                due_date=datetime.combine(loan_date + timedelta(days=30), datetime.min.time()),
                duration_days=30,
                status='active',
                is_deleted=False,
                is_rolled_over=False
            )
            matching_loans.append(loan)
        
        # Apply filters
        queryset = Loan.objects.filter(loan_number__startswith='LOAN-TEST-')
        queryset = ReportFilterService.apply_date_range_filter(
            queryset, start_date, end_date, 'disbursement_date'
        )
        queryset = ReportFilterService.apply_loan_status_filter(
            queryset, exclude_rolled_over=True, exclude_deleted=True
        )
        queryset = ReportFilterService.apply_loan_product_filter(
            queryset, str(product.id)
        )
        
        # Verify ALL matching loans are included (no false negatives)
        print(f"✓ Found {queryset.count()} loans matching all criteria")
        filtered_ids = set(queryset.values_list('id', flat=True))
        for loan in matching_loans:
            assert loan.id in filtered_ids, \
                f"FAIL: Loan {loan.loan_number} matches all criteria but was excluded (false negative)"
            print(f"  ✓ Loan {loan.loan_number} correctly included")
        
        # Verify the count matches
        assert queryset.count() == len(matching_loans), \
            f"FAIL: Expected {len(matching_loans)} loans, got {queryset.count()}"
        
        print("✓ Property 25 PASSED: Filter completeness works correctly (no false negatives)")
        return True
        
    except Exception as e:
        print(f"✗ Property 25 FAILED: {str(e)}")
        import traceback
        traceback.print_exc()
        return False
    finally:
        cleanup()


if __name__ == '__main__':
    print("\n" + "="*80)
    print("TASK 7 PROPERTY TESTS")
    print("Testing comprehensive filtering for loans due report")
    print("="*80)
    
    results = []
    
    # Run all tests
    results.append(('Property 23: Daily filter precision', test_daily_filter_precision()))
    results.append(('Property 24: Weekly filter range', test_weekly_filter_range()))
    results.append(('Property 25: Filter completeness', test_filter_completeness()))
    
    # Summary
    print("\n" + "="*80)
    print("TEST SUMMARY")
    print("="*80)
    
    passed = sum(1 for _, result in results if result)
    total = len(results)
    
    for name, result in results:
        status = "✓ PASSED" if result else "✗ FAILED"
        print(f"{status}: {name}")
    
    print(f"\nTotal: {passed}/{total} tests passed")
    
    if passed == total:
        print("\n✓ ALL PROPERTY TESTS PASSED!")
        sys.exit(0)
    else:
        print(f"\n✗ {total - passed} PROPERTY TEST(S) FAILED")
        sys.exit(1)

