"""
Unit tests for Demographic Analysis Service

Tests age calculation, age group categorization, gender categorization,
and payment pattern classification.
"""

from datetime import date, timedelta
from decimal import Decimal
from django.test import TestCase
from django.utils import timezone
from django.contrib.auth import get_user_model
from loans.models import Loan, LoanProduct, LoanApplication, Repayment
from users.models import Branch
from reports.demographic_service import DemographicAnalysisService
import uuid

User = get_user_model()


class DemographicAnalysisServiceTest(TestCase):
    """Test cases for DemographicAnalysisService"""
    
    def setUp(self):
        """Set up test data"""
        # Create a branch
        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TEST001',
            is_active=True
        )
        
        # Create a loan product
        self.loan_product = LoanProduct.objects.create(
            name='Test Product',
            product_type='boost',
            description='Test product',
            min_amount=Decimal('1000.00'),
            max_amount=Decimal('100000.00'),
            interest_rate=Decimal('10.00'),
            processing_fee=Decimal('5.00'),
            min_duration=7,
            max_duration=90
        )
    
    def test_calculate_age_with_valid_birth_date(self):
        """Test age calculation with valid birth date"""
        birth_date = date(1990, 6, 15)
        reference_date = date(2024, 6, 15)
        
        age = DemographicAnalysisService.calculate_age(birth_date, reference_date)
        
        self.assertEqual(age, 34)
    
    def test_calculate_age_before_birthday(self):
        """Test age calculation before birthday in current year"""
        birth_date = date(1990, 12, 31)
        reference_date = date(2024, 1, 1)
        
        age = DemographicAnalysisService.calculate_age(birth_date, reference_date)
        
        self.assertEqual(age, 33)  # Birthday hasn't occurred yet
    
    def test_calculate_age_after_birthday(self):
        """Test age calculation after birthday in current year"""
        birth_date = date(1990, 1, 1)
        reference_date = date(2024, 12, 31)
        
        age = DemographicAnalysisService.calculate_age(birth_date, reference_date)
        
        self.assertEqual(age, 34)  # Birthday has occurred
    
    def test_calculate_age_with_none_birth_date(self):
        """Test age calculation with None birth date"""
        age = DemographicAnalysisService.calculate_age(None)
        
        self.assertIsNone(age)
    
    def test_categorize_age_group_18_25(self):
        """Test age group categorization for 18-25 range"""
        birth_date = date(2000, 1, 1)
        reference_date = date(2024, 1, 1)
        
        age_group = DemographicAnalysisService.categorize_age_group(birth_date, reference_date)
        
        self.assertEqual(age_group, '18-25')
    
    def test_categorize_age_group_26_35(self):
        """Test age group categorization for 26-35 range"""
        birth_date = date(1990, 1, 1)
        reference_date = date(2024, 1, 1)
        
        age_group = DemographicAnalysisService.categorize_age_group(birth_date, reference_date)
        
        self.assertEqual(age_group, '26-35')
    
    def test_categorize_age_group_36_45(self):
        """Test age group categorization for 36-45 range"""
        birth_date = date(1980, 1, 1)
        reference_date = date(2024, 1, 1)
        
        age_group = DemographicAnalysisService.categorize_age_group(birth_date, reference_date)
        
        self.assertEqual(age_group, '36-45')
    
    def test_categorize_age_group_46_55(self):
        """Test age group categorization for 46-55 range"""
        birth_date = date(1970, 1, 1)
        reference_date = date(2024, 1, 1)
        
        age_group = DemographicAnalysisService.categorize_age_group(birth_date, reference_date)
        
        self.assertEqual(age_group, '46-55')
    
    def test_categorize_age_group_56_65(self):
        """Test age group categorization for 56-65 range"""
        birth_date = date(1960, 1, 1)
        reference_date = date(2024, 1, 1)
        
        age_group = DemographicAnalysisService.categorize_age_group(birth_date, reference_date)
        
        self.assertEqual(age_group, '56-65')
    
    def test_categorize_age_group_66_plus(self):
        """Test age group categorization for 66+ range"""
        birth_date = date(1950, 1, 1)
        reference_date = date(2024, 1, 1)
        
        age_group = DemographicAnalysisService.categorize_age_group(birth_date, reference_date)
        
        self.assertEqual(age_group, '66+')
    
    def test_categorize_age_group_not_specified(self):
        """Test age group categorization with None birth date"""
        age_group = DemographicAnalysisService.categorize_age_group(None)
        
        self.assertEqual(age_group, 'Not Specified')
    
    def test_categorize_age_group_below_18(self):
        """Test age group categorization for age below 18"""
        birth_date = date(2010, 1, 1)
        reference_date = date(2024, 1, 1)
        
        age_group = DemographicAnalysisService.categorize_age_group(birth_date, reference_date)
        
        self.assertEqual(age_group, 'Not Specified')
    
    def test_categorize_gender_male(self):
        """Test gender categorization for male"""
        gender_category = DemographicAnalysisService.categorize_gender('M')
        
        self.assertEqual(gender_category, 'Male')
    
    def test_categorize_gender_female(self):
        """Test gender categorization for female"""
        gender_category = DemographicAnalysisService.categorize_gender('F')
        
        self.assertEqual(gender_category, 'Female')
    
    def test_categorize_gender_other(self):
        """Test gender categorization for other"""
        gender_category = DemographicAnalysisService.categorize_gender('O')
        
        self.assertEqual(gender_category, 'Other')
    
    def test_categorize_gender_none(self):
        """Test gender categorization with None"""
        gender_category = DemographicAnalysisService.categorize_gender(None)
        
        self.assertEqual(gender_category, 'Not Specified')
    
    def test_categorize_gender_empty_string(self):
        """Test gender categorization with empty string"""
        gender_category = DemographicAnalysisService.categorize_gender('')
        
        self.assertEqual(gender_category, 'Not Specified')
    
    def test_classify_payment_pattern_on_time(self):
        """Test payment pattern classification for on-time payment"""
        # Create borrower
        borrower = User.objects.create_user(
            username='testuser',
            phone_number='+254700000001',
            email='testuser@example.com',
            date_of_birth=date(1990, 1, 1),
            gender='M',
            branch=self.branch
        )
        
        # Create loan application
        application = LoanApplication.objects.create(
            application_number='APP-000001',
            borrower=borrower,
            loan_product=self.loan_product,
            requested_amount=Decimal('10000.00'),
            requested_duration=30,
            purpose='Test loan',
            status='approved'
        )
        
        # Create loan
        loan = Loan.objects.create(
            loan_number='LOAN001',
            application=application,
            borrower=borrower,
            principal_amount=Decimal('10000.00'),
            interest_amount=Decimal('1000.00'),
            processing_fee=Decimal('500.00'),
            total_amount=Decimal('11500.00'),
            disbursement_date=timezone.now() - timedelta(days=30),
            due_date=timezone.now() - timedelta(days=1),
            duration_days=30,
            status='active'
        )
        
        # Create on-time repayment
        Repayment.objects.create(
            loan=loan,
            amount=Decimal('11500.00'),
            payment_date=loan.due_date - timedelta(days=1),
            payment_method='mpesa'
        )
        
        pattern = DemographicAnalysisService.classify_payment_pattern(loan)
        
        self.assertEqual(pattern, 'on-time')
    
    def test_classify_payment_pattern_late(self):
        """Test payment pattern classification for late payment"""
        # Create borrower
        borrower = User.objects.create_user(
            username='testuser2',
            phone_number='+254700000002',
            email='testuser2@example.com',
            date_of_birth=date(1990, 1, 1),
            gender='F',
            branch=self.branch
        )
        
        # Create loan application
        application = LoanApplication.objects.create(
            application_number='APP-000002',
            borrower=borrower,
            loan_product=self.loan_product,
            requested_amount=Decimal('10000.00'),
            requested_duration=30,
            purpose='Test loan',
            status='approved'
        )
        
        # Create loan
        loan = Loan.objects.create(
            loan_number='LOAN002',
            application=application,
            borrower=borrower,
            principal_amount=Decimal('10000.00'),
            interest_amount=Decimal('1000.00'),
            processing_fee=Decimal('500.00'),
            total_amount=Decimal('11500.00'),
            disbursement_date=timezone.now() - timedelta(days=30),
            due_date=timezone.now() - timedelta(days=10),
            duration_days=30,
            status='active'
        )
        
        # Create late repayment
        Repayment.objects.create(
            loan=loan,
            amount=Decimal('11500.00'),
            payment_date=loan.due_date + timedelta(days=5),
            payment_method='mpesa'
        )
        
        pattern = DemographicAnalysisService.classify_payment_pattern(loan)
        
        self.assertEqual(pattern, 'late')
    
    def test_classify_payment_pattern_no_payment_past_due(self):
        """Test payment pattern classification for no payment past due date"""
        # Create borrower
        borrower = User.objects.create_user(
            username='testuser3',
            phone_number='+254700000003',
            email='testuser3@example.com',
            date_of_birth=date(1990, 1, 1),
            gender='M',
            branch=self.branch
        )
        
        # Create loan application
        application = LoanApplication.objects.create(
            application_number='APP-000003',
            borrower=borrower,
            loan_product=self.loan_product,
            requested_amount=Decimal('10000.00'),
            requested_duration=30,
            purpose='Test loan',
            status='approved'
        )
        
        # Create loan
        loan = Loan.objects.create(
            loan_number='LOAN003',
            application=application,
            borrower=borrower,
            principal_amount=Decimal('10000.00'),
            interest_amount=Decimal('1000.00'),
            processing_fee=Decimal('500.00'),
            total_amount=Decimal('11500.00'),
            disbursement_date=timezone.now() - timedelta(days=30),
            due_date=timezone.now() - timedelta(days=5),
            duration_days=30,
            status='active'
        )
        
        pattern = DemographicAnalysisService.classify_payment_pattern(loan)
        
        self.assertEqual(pattern, 'late')
    
    def test_classify_payment_pattern_no_payment_not_due(self):
        """Test payment pattern classification for no payment not yet due"""
        # Create borrower
        borrower = User.objects.create_user(
            username='testuser4',
            phone_number='+254700000004',
            email='testuser4@example.com',
            date_of_birth=date(1990, 1, 1),
            gender='F',
            branch=self.branch
        )
        
        # Create loan application
        application = LoanApplication.objects.create(
            application_number='APP-000004',
            borrower=borrower,
            loan_product=self.loan_product,
            requested_amount=Decimal('10000.00'),
            requested_duration=30,
            purpose='Test loan',
            status='approved'
        )
        
        # Create loan
        loan = Loan.objects.create(
            loan_number='LOAN004',
            application=application,
            borrower=borrower,
            principal_amount=Decimal('10000.00'),
            interest_amount=Decimal('1000.00'),
            processing_fee=Decimal('500.00'),
            total_amount=Decimal('11500.00'),
            disbursement_date=timezone.now(),
            due_date=timezone.now() + timedelta(days=30),
            duration_days=30,
            status='active'
        )
        
        pattern = DemographicAnalysisService.classify_payment_pattern(loan)
        
        self.assertEqual(pattern, 'pending')
    
    def test_analyze_by_age_group(self):
        """Test analysis by age group"""
        # Create borrowers with different ages
        borrower1 = User.objects.create_user(
            username='user1',
            phone_number='+254700000011',
            email='user1@example.com',
            date_of_birth=date(2000, 1, 1),  # 24 years old
            gender='M',
            branch=self.branch
        )
        
        borrower2 = User.objects.create_user(
            username='user2',
            phone_number='+254700000012',
            email='user2@example.com',
            date_of_birth=date(1990, 1, 1),  # 34 years old
            gender='F',
            branch=self.branch
        )
        
        # Create loan applications
        app1 = LoanApplication.objects.create(
            application_number='APP-000011',
            borrower=borrower1,
            loan_product=self.loan_product,
            requested_amount=Decimal('10000.00'),
            requested_duration=30,
            purpose='Test loan',
            status='approved'
        )
        
        app2 = LoanApplication.objects.create(
            application_number='APP-000012',
            borrower=borrower2,
            loan_product=self.loan_product,
            requested_amount=Decimal('20000.00'),
            requested_duration=30,
            purpose='Test loan',
            status='approved'
        )
        
        # Create loans
        loan1 = Loan.objects.create(
            loan_number='LOAN011',
            application=app1,
            borrower=borrower1,
            principal_amount=Decimal('10000.00'),
            interest_amount=Decimal('1000.00'),
            processing_fee=Decimal('500.00'),
            total_amount=Decimal('11500.00'),
            disbursement_date=timezone.now() - timedelta(days=30),
            due_date=timezone.now() - timedelta(days=1),
            duration_days=30,
            status='active'
        )
        
        loan2 = Loan.objects.create(
            loan_number='LOAN012',
            application=app2,
            borrower=borrower2,
            principal_amount=Decimal('20000.00'),
            interest_amount=Decimal('2000.00'),
            processing_fee=Decimal('1000.00'),
            total_amount=Decimal('23000.00'),
            disbursement_date=timezone.now() - timedelta(days=30),
            due_date=timezone.now() - timedelta(days=1),
            duration_days=30,
            status='active'
        )
        
        # Analyze
        results = DemographicAnalysisService.analyze_by_age_group(
            Loan.objects.all(),
            reference_date=date(2024, 1, 1)
        )
        
        # Check 18-25 group
        self.assertEqual(results['18-25']['total_applications'], 1)
        self.assertEqual(results['18-25']['approved_applications'], 1)
        
        # Check 26-35 group
        self.assertEqual(results['26-35']['total_applications'], 1)
        self.assertEqual(results['26-35']['approved_applications'], 1)
    
    def test_analyze_by_gender(self):
        """Test analysis by gender"""
        # Create borrowers with different genders
        borrower1 = User.objects.create_user(
            username='user21',
            phone_number='+254700000021',
            email='user21@example.com',
            date_of_birth=date(1990, 1, 1),
            gender='M',
            branch=self.branch
        )
        
        borrower2 = User.objects.create_user(
            username='user22',
            phone_number='+254700000022',
            email='user22@example.com',
            date_of_birth=date(1990, 1, 1),
            gender='F',
            branch=self.branch
        )
        
        # Create loan applications
        app1 = LoanApplication.objects.create(
            application_number='APP-000021',
            borrower=borrower1,
            loan_product=self.loan_product,
            requested_amount=Decimal('10000.00'),
            requested_duration=30,
            purpose='Test loan',
            status='approved'
        )
        
        app2 = LoanApplication.objects.create(
            application_number='APP-000022',
            borrower=borrower2,
            loan_product=self.loan_product,
            requested_amount=Decimal('20000.00'),
            requested_duration=30,
            purpose='Test loan',
            status='approved'
        )
        
        # Create loans
        loan1 = Loan.objects.create(
            loan_number='LOAN021',
            application=app1,
            borrower=borrower1,
            principal_amount=Decimal('10000.00'),
            interest_amount=Decimal('1000.00'),
            processing_fee=Decimal('500.00'),
            total_amount=Decimal('11500.00'),
            disbursement_date=timezone.now() - timedelta(days=30),
            due_date=timezone.now() - timedelta(days=1),
            duration_days=30,
            status='active'
        )
        
        loan2 = Loan.objects.create(
            loan_number='LOAN022',
            application=app2,
            borrower=borrower2,
            principal_amount=Decimal('20000.00'),
            interest_amount=Decimal('2000.00'),
            processing_fee=Decimal('1000.00'),
            total_amount=Decimal('23000.00'),
            disbursement_date=timezone.now() - timedelta(days=30),
            due_date=timezone.now() - timedelta(days=1),
            duration_days=30,
            status='active'
        )
        
        # Analyze
        results = DemographicAnalysisService.analyze_by_gender(Loan.objects.all())
        
        # Check Male group
        self.assertEqual(results['Male']['total_applications'], 1)
        self.assertEqual(results['Male']['approved_applications'], 1)
        
        # Check Female group
        self.assertEqual(results['Female']['total_applications'], 1)
        self.assertEqual(results['Female']['approved_applications'], 1)
