"""
Repayment Scheduling Service

This service handles proper repayment scheduling and arrears calculation
based on the specific repayment method (daily, weekly, monthly) for each loan.
"""

from datetime import datetime, timedelta, date
from decimal import Decimal
from django.utils import timezone
from django.db.models import Sum, Q
from .models import Loan, Repayment


class RepaymentScheduler:
    """
    Service for calculating repayment schedules and arrears based on repayment method
    """
    
    @staticmethod
    def get_repayment_method(loan):
        """Get the repayment method for a loan"""
        if hasattr(loan, 'application') and loan.application:
            return loan.application.repayment_method
        return 'monthly'  # Default fallback
    
    @staticmethod
    def calculate_expected_payment_amount(loan):
        """Calculate the expected payment amount per period based on repayment method"""
        repayment_method = RepaymentScheduler.get_repayment_method(loan)
        total_amount = loan.total_amount or Decimal('0.00')

        if not total_amount or total_amount <= 0:
            return Decimal('0.00')

        if repayment_method == 'daily':
            # For daily payments, divide by total duration in days
            return total_amount / loan.duration_days if loan.duration_days and loan.duration_days > 0 else total_amount
        elif repayment_method == 'weekly':
            # For weekly payments, divide by number of weeks
            weeks = max(1, (loan.duration_days or 30) // 7)
            return total_amount / weeks
        else:  # monthly
            # For monthly payments, divide by number of months
            months = max(1, (loan.duration_days or 30) // 30)
            return total_amount / months
    
    @staticmethod
    def get_payment_period_days(repayment_method):
        """Get the number of days between expected payments"""
        if repayment_method == 'daily':
            return 1
        elif repayment_method == 'weekly':
            return 7
        else:  # monthly
            return 30
    
    @staticmethod
    def generate_payment_schedule(loan):
        """Generate expected payment schedule for a loan"""
        repayment_method = RepaymentScheduler.get_repayment_method(loan)
        period_days = RepaymentScheduler.get_payment_period_days(repayment_method)
        expected_amount = RepaymentScheduler.calculate_expected_payment_amount(loan)
        
        schedule = []
        current_date = loan.disbursement_date.date() if loan.disbursement_date else date.today()
        total_periods = loan.duration_days // period_days
        
        for period in range(total_periods):
            # Start from first payment period (period + 1), not disbursement date
            due_date = current_date + timedelta(days=(period + 1) * period_days)
            
            # Get actual payments made during this period
            period_start = due_date
            period_end = due_date + timedelta(days=period_days - 1)
            
            actual_payments = Repayment.objects.filter(
                loan=loan,
                payment_date__date__gte=period_start,
                payment_date__date__lte=period_end
            ).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
            
            schedule.append({
                'period': period + 1,
                'due_date': due_date,
                'expected_amount': expected_amount,
                'actual_amount': actual_payments,
                'difference': actual_payments - expected_amount,
                'is_paid': actual_payments >= expected_amount,
                'is_overdue': due_date < date.today() and actual_payments < expected_amount
            })
        
        return schedule
    
    @staticmethod
    def calculate_arrears_amount(loan):
        """Calculate the actual arrears amount based on repayment method and missed payments"""
        try:
            repayment_method = RepaymentScheduler.get_repayment_method(loan)
            period_days = RepaymentScheduler.get_payment_period_days(repayment_method)
            expected_amount = RepaymentScheduler.calculate_expected_payment_amount(loan)

            if not expected_amount or expected_amount <= 0:
                return Decimal('0.00')

            # Get the disbursement date
            disbursement_date = loan.disbursement_date.date() if loan.disbursement_date else date.today()

            # Calculate how many payment periods have passed
            # First payment is due after one period, not immediately
            days_since_disbursement = (date.today() - disbursement_date).days

            # If we haven't reached the first payment date yet, no arrears
            if days_since_disbursement < period_days:
                return Decimal('0.00')

            # Calculate periods that are actually due (not including current period if not complete)
            periods_passed = days_since_disbursement // period_days

            # Calculate total expected payments up to now
            total_expected = expected_amount * periods_passed

            # Get total actual payments
            total_actual = Repayment.objects.filter(loan=loan).aggregate(
                total=Sum('amount')
            )['total'] or Decimal('0.00')

            # Arrears is the difference
            arrears = total_expected - total_actual
            return max(Decimal('0.00'), arrears)
        except Exception:
            return Decimal('0.00')
    
    @staticmethod
    def get_missed_payment_periods(loan):
        """Get list of missed payment periods with details"""
        repayment_method = RepaymentScheduler.get_repayment_method(loan)
        period_days = RepaymentScheduler.get_payment_period_days(repayment_method)
        expected_amount = RepaymentScheduler.calculate_expected_payment_amount(loan)
        
        disbursement_date = loan.disbursement_date.date() if loan.disbursement_date else date.today()
        
        # Use the loan's actual due_date if available, otherwise calculate
        if loan.due_date:
            # Convert to date if it's a datetime
            loan_due_date = loan.due_date.date() if hasattr(loan.due_date, 'date') else loan.due_date
            # If the loan's due date hasn't passed yet, no missed periods
            if loan_due_date > date.today():
                return []
            
            # Check if loan is fully paid
            total_paid = Repayment.objects.filter(loan=loan).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
            if total_paid >= loan.total_amount:
                return []
            
            # Loan is overdue - return single missed period for the full amount
            days_overdue = (date.today() - loan_due_date).days
            return [{
                'period': 1,
                'due_date': loan_due_date,
                'expected_amount': loan.total_amount,
                'actual_amount': total_paid,
                'missed_amount': loan.total_amount - total_paid,
                'days_overdue': days_overdue,
                'repayment_method': repayment_method
            }]
        
        # Fallback to period-based calculation for loans without due_date
        days_since_disbursement = (date.today() - disbursement_date).days
        periods_passed = days_since_disbursement // period_days
        
        missed_periods = []
        
        for period in range(1, periods_passed + 1):
            # First payment is due after one period, not on disbursement date
            due_date = disbursement_date + timedelta(days=period * period_days)
            
            # Only check periods where due date has passed
            if due_date > date.today():
                continue
            
            # Get payments for this period
            period_start = due_date
            period_end = due_date + timedelta(days=period_days - 1)
            
            actual_payments = Repayment.objects.filter(
                loan=loan,
                payment_date__date__gte=period_start,
                payment_date__date__lte=period_end
            ).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
            
            if actual_payments < expected_amount:
                missed_amount = expected_amount - actual_payments
                days_overdue = (date.today() - due_date).days
                
                missed_periods.append({
                    'period': period,
                    'due_date': due_date,
                    'expected_amount': expected_amount,
                    'actual_amount': actual_payments,
                    'missed_amount': missed_amount,
                    'days_overdue': days_overdue,
                    'repayment_method': repayment_method
                })
        
        return missed_periods
    
    @staticmethod
    def is_loan_in_arrears(loan):
        """Check if a loan is in arrears based on repayment method"""
        arrears_amount = RepaymentScheduler.calculate_arrears_amount(loan)
        return arrears_amount > Decimal('0.00')
    
    @staticmethod
    def get_arrears_summary(loan):
        """Get comprehensive arrears summary for a loan"""
        repayment_method = RepaymentScheduler.get_repayment_method(loan)
        arrears_amount = RepaymentScheduler.calculate_arrears_amount(loan)
        missed_periods = RepaymentScheduler.get_missed_payment_periods(loan)
        
        # Calculate days overdue (from first missed payment)
        days_overdue = 0
        days_missed = 0
        
        if missed_periods:
            first_missed = min(missed_periods, key=lambda x: x['due_date'])
            days_overdue = first_missed['days_overdue']
            
            # Calculate total days missed across all missed periods
            period_days = RepaymentScheduler.get_payment_period_days(repayment_method)
            days_missed = len(missed_periods) * period_days
        
        return {
            'is_in_arrears': arrears_amount > Decimal('0.00'),
            'arrears_amount': arrears_amount,
            'repayment_method': repayment_method,
            'missed_periods_count': len(missed_periods),
            'days_overdue': days_overdue,
            'days_missed': days_missed,
            'missed_periods': missed_periods,
            'expected_payment_amount': RepaymentScheduler.calculate_expected_payment_amount(loan),
            'payment_frequency': RepaymentScheduler.get_payment_period_days(repayment_method)
        }
    
    @staticmethod
    def get_loans_in_arrears(queryset=None, branch_id=None):
        """Get all loans that are in arrears based on proper repayment method calculation"""
        if queryset is None:
            queryset = Loan.objects.filter(status__in=['active', 'rolled_over', 'defaulted'])
        
        if branch_id:
            queryset = queryset.filter(borrower__branch_id=branch_id)
        
        loans_in_arrears = []
        
        for loan in queryset:
            arrears_summary = RepaymentScheduler.get_arrears_summary(loan)
            if arrears_summary['is_in_arrears']:
                loans_in_arrears.append({
                    'loan': loan,
                    'arrears_summary': arrears_summary
                })
        
        return loans_in_arrears
    
    @staticmethod
    def get_next_payment_due_date(loan):
        """Get the next payment due date for a loan"""
        repayment_method = RepaymentScheduler.get_repayment_method(loan)
        period_days = RepaymentScheduler.get_payment_period_days(repayment_method)
        
        disbursement_date = loan.disbursement_date.date() if loan.disbursement_date else date.today()
        days_since_disbursement = (date.today() - disbursement_date).days
        periods_passed = days_since_disbursement // period_days
        
        # Next period
        next_period = periods_passed + 1
        next_due_date = disbursement_date + timedelta(days=(next_period - 1) * period_days)
        
        return next_due_date
    
    @staticmethod
    def get_payment_status(loan):
        """Get current payment status for a loan"""
        repayment_method = RepaymentScheduler.get_repayment_method(loan)
        arrears_summary = RepaymentScheduler.get_arrears_summary(loan)
        next_due_date = RepaymentScheduler.get_next_payment_due_date(loan)
        
        if not arrears_summary['is_in_arrears']:
            return 'current'
        elif arrears_summary['days_overdue'] <= 7:
            return 'slightly_overdue'
        elif arrears_summary['days_overdue'] <= 30:
            return 'overdue'
        else:
            return 'severely_overdue'
