"""
Missed Payments Service for tracking and reporting missed payment history.

This service provides functionality to:
- Identify clients with missed payments
- Retrieve detailed missed payment history for individual clients
- Calculate missed payment counts
- Sort and filter missed payments
"""

from typing import List, Dict, Optional
from datetime import date
from django.db.models import Q, Sum, Count
from django.utils import timezone
from decimal import Decimal

from loans.models import Loan
from users.models import CustomUser
from .query_optimizer import QueryOptimizer


class MissedPaymentsService:
    """
    Service for tracking and reporting missed payments.
    
    A missed payment is defined as a loan where:
    - due_date < current_date AND
    - outstanding_amount > 0
    """
    
    @staticmethod
    def get_clients_with_missed_payments(branch_id: Optional[str] = None) -> List[Dict]:
        """
        Get all clients who have loans with missed payments.
        
        Args:
            branch_id: Optional branch ID to filter by
            
        Returns:
            List of dictionaries containing client information and missed payment counts
        """
        today = timezone.now().date()
        
        # Base queryset for loans with missed payments (optimized)
        missed_loans_qs = QueryOptimizer.get_optimized_loans_queryset(
            Loan.objects.filter(
                is_deleted=False,
                due_date__lt=today
            ).exclude(
                Q(status='rolled_over') | Q(is_rolled_over=True)
            )
        )
        
        # Filter by branch if specified
        if branch_id:
            missed_loans_qs = missed_loans_qs.filter(borrower__branch_id=branch_id)
        
        # Filter loans with outstanding balance > 0
        loans_with_missed_payments = []
        for loan in missed_loans_qs:
            if loan.outstanding_amount > 0:
                loans_with_missed_payments.append(loan)
        
        # Group by client
        clients_dict = {}
        for loan in loans_with_missed_payments:
            client_id = str(loan.borrower.id)
            if client_id not in clients_dict:
                clients_dict[client_id] = {
                    'client': loan.borrower,
                    'client_id': client_id,
                    'client_name': loan.borrower.get_full_name(),
                    'phone_number': loan.borrower.phone_number,
                    'missed_payment_count': 0,
                    'total_outstanding': Decimal('0'),
                    'loans_with_missed_payments': []
                }
            
            clients_dict[client_id]['missed_payment_count'] += 1
            clients_dict[client_id]['total_outstanding'] += loan.outstanding_amount
            clients_dict[client_id]['loans_with_missed_payments'].append(loan)
        
        # Convert to list and sort by missed payment count (descending)
        clients_list = list(clients_dict.values())
        clients_list.sort(key=lambda x: x['missed_payment_count'], reverse=True)
        
        return clients_list
    
    @staticmethod
    def get_missed_payments_for_client(client_id: str) -> List[Dict]:
        """
        Get all missed payments for a specific client.
        
        Args:
            client_id: The client's user ID
            
        Returns:
            List of dictionaries containing missed payment details,
            sorted by due_date in descending order (most recent first)
        """
        today = timezone.now().date()
        
        # Get all loans for this client with missed payments (optimized)
        missed_loans = QueryOptimizer.get_optimized_loans_queryset(
            Loan.objects.filter(
                borrower_id=client_id,
                is_deleted=False,
                due_date__lt=today
            ).exclude(
                Q(status='rolled_over') | Q(is_rolled_over=True)
            )
        ).order_by('-due_date')
        
        # Build list of missed payments
        missed_payments = []
        for loan in missed_loans:
            # Only include if outstanding amount > 0
            if loan.outstanding_amount > 0:
                # Calculate days overdue
                if hasattr(loan.due_date, 'date'):
                    due_date_obj = loan.due_date.date()
                else:
                    due_date_obj = loan.due_date
                
                days_overdue = (today - due_date_obj).days
                
                missed_payments.append({
                    'loan': loan,
                    'loan_number': loan.loan_number,
                    'loan_product': loan.application.loan_product.name if loan.application else 'N/A',
                    'due_date': due_date_obj,
                    'amount_due': loan.outstanding_amount,
                    'days_overdue': days_overdue,
                    'payment_status': 'overdue',
                    'total_amount': loan.total_amount,
                    'amount_paid': loan.amount_paid,
                })
        
        return missed_payments
    
    @staticmethod
    def calculate_missed_payment_count(client: CustomUser) -> int:
        """
        Calculate the number of missed payments for a client.
        
        Args:
            client: The CustomUser instance
            
        Returns:
            Count of loans with missed payments
        """
        today = timezone.now().date()
        
        # Get all loans for this client with missed payments
        missed_loans = Loan.objects.filter(
            borrower=client,
            is_deleted=False,
            due_date__lt=today
        ).exclude(
            Q(status='rolled_over') | Q(is_rolled_over=True)
        )
        
        # Count loans with outstanding balance > 0
        count = 0
        for loan in missed_loans:
            if loan.outstanding_amount > 0:
                count += 1
        
        return count
    
    @staticmethod
    def get_missed_payment_details(loan: Loan) -> List[Dict]:
        """
        Get detailed missed payment information for a specific loan.
        
        This method provides granular details about missed payment periods
        for loans with scheduled repayments.
        
        Args:
            loan: The Loan instance
            
        Returns:
            List of dictionaries containing missed payment period details
        """
        today = timezone.now().date()
        
        # Check if loan has missed payments
        if hasattr(loan.due_date, 'date'):
            due_date_obj = loan.due_date.date()
        else:
            due_date_obj = loan.due_date
        
        if due_date_obj >= today or loan.outstanding_amount <= 0:
            return []
        
        # For now, return a single missed payment entry
        # In the future, this could be expanded to handle scheduled repayments
        days_overdue = (today - due_date_obj).days
        
        return [{
            'due_date': due_date_obj,
            'expected_amount': loan.outstanding_amount,
            'days_overdue': days_overdue,
            'status': 'missed'
        }]
