"""
Celery tasks for accounting operations.

This module implements periodic tasks for:
- Daily interest accrual on active loans
- Account balance caching for performance optimization
"""

import logging
from decimal import Decimal
from datetime import date, timedelta
from django.utils import timezone
from celery import shared_task
from django.db import transaction

logger = logging.getLogger(__name__)


@shared_task(bind=True)
def accrue_loan_interest(self):
    """
    Accrue interest for all active loans.
    
    This task runs daily to create interest accrual entries for all active loans.
    It queries all loans with status='active' and is_deleted=False, calculates
    daily interest for each loan, and creates journal entries for non-zero interest.
    
    The task continues processing even if individual entries fail, logging errors
    for later review.
    
    Validates: Requirements 3.1, 3.2, 3.6, 3.7, 11.4
    
    Returns:
        dict: Summary of processing results
    """
    from loans.models import Loan
    from accounting.services.integration_service import IntegrationService
    from accounting.services.accounting_service import AccountingService
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    integration_service = IntegrationService()
    accounting_service = AccountingService()
    
    # Get system user for auto-posting (or None if not available)
    system_user = User.objects.filter(username='system').first()
    
    # Statistics
    processed_count = 0
    accrued_count = 0
    skipped_count = 0
    error_count = 0
    total_interest_accrued = Decimal('0.00')
    errors = []
    
    # Get today's date
    today = timezone.now().date()
    
    logger.info(f"Starting daily interest accrual for {today}")
    
    # Query all active loans
    active_loans = Loan.objects.filter(
        status='active',
        is_deleted=False
    ).select_related('application__loan_product', 'borrower__branch')
    
    logger.info(f"Found {active_loans.count()} active loans to process")
    
    for loan in active_loans:
        processed_count += 1
        
        try:
            # Calculate daily interest
            daily_interest = calculate_daily_interest(loan)
            
            if daily_interest <= Decimal('0.00'):
                skipped_count += 1
                logger.debug(
                    f"Skipped loan {loan.loan_number}: "
                    f"zero or negative interest ({daily_interest})"
                )
                continue
            
            # Create interest accrual entry
            journal_entry = integration_service.create_interest_accrual_entry(
                loan=loan,
                interest_amount=daily_interest,
                accrual_date=today
            )
            
            # Auto-post the accrual entry
            accounting_service.post_journal_entry(journal_entry, system_user)
            
            accrued_count += 1
            total_interest_accrued += daily_interest
            
            logger.debug(
                f"Accrued {daily_interest} for loan {loan.loan_number}"
            )
            
        except Exception as e:
            error_count += 1
            error_msg = (
                f"Error processing loan {loan.loan_number}: {str(e)}"
            )
            logger.error(error_msg, exc_info=True)
            errors.append(error_msg)
            # Continue processing other loans
            continue
    
    # Log summary
    summary = {
        'date': str(today),
        'processed': processed_count,
        'accrued': accrued_count,
        'skipped': skipped_count,
        'errors': error_count,
        'total_interest_accrued': str(total_interest_accrued),
        'error_details': errors[:10]  # Limit to first 10 errors
    }
    
    logger.info(
        f"Completed daily interest accrual. "
        f"Processed: {processed_count}, "
        f"Accrued: {accrued_count}, "
        f"Skipped: {skipped_count}, "
        f"Errors: {error_count}, "
        f"Total Interest: {total_interest_accrued}"
    )
    
    return summary


def calculate_daily_interest(loan) -> Decimal:
    """
    Calculate daily interest for a loan.
    
    This function calculates the daily interest accrual based on the loan's
    interest rate and outstanding balance. The calculation uses simple daily
    interest: (outstanding_balance * annual_rate / 365).
    
    Args:
        loan: The Loan instance
        
    Returns:
        Decimal: The daily interest amount, rounded to 2 decimal places
        
    Validates: Requirements 3.1, 3.2, 11.4
    """
    try:
        # Get the loan's interest rate (monthly rate from product)
        monthly_rate = loan.application.loan_product.get_interest_rate()
        
        # Convert monthly rate to daily rate
        # Annual rate = monthly_rate * 12
        # Daily rate = annual_rate / 365
        annual_rate = Decimal(str(monthly_rate)) * Decimal('12')
        daily_rate = annual_rate / Decimal('365') / Decimal('100')
        
        # Calculate daily interest on outstanding balance
        outstanding_balance = loan.outstanding_amount
        daily_interest = outstanding_balance * daily_rate
        
        # Round to 2 decimal places
        return daily_interest.quantize(Decimal('0.01'))
        
    except Exception as e:
        logger.error(
            f"Error calculating daily interest for loan {loan.loan_number}: {str(e)}"
        )
        return Decimal('0.00')


@shared_task(bind=True)
def cache_account_balances(self):
    """
    Cache account balances for performance optimization.
    
    This task runs daily to calculate and cache account balances as of yesterday.
    It creates or updates AccountBalance records for all active accounts, storing
    debit_balance, credit_balance, and net_balance for efficient reporting.
    
    Validates: Requirements 20.4
    
    Returns:
        dict: Summary of caching results
    """
    from accounting.models import Account, AccountBalance
    from accounting.services.accounting_service import AccountingService
    
    accounting_service = AccountingService()
    
    # Statistics
    processed_count = 0
    created_count = 0
    updated_count = 0
    error_count = 0
    errors = []
    
    # Calculate as of yesterday
    yesterday = timezone.now().date() - timedelta(days=1)
    
    logger.info(f"Starting account balance caching for {yesterday}")
    
    # Get all active accounts
    active_accounts = Account.objects.filter(is_active=True)
    
    logger.info(f"Found {active_accounts.count()} active accounts to process")
    
    for account in active_accounts:
        processed_count += 1
        
        try:
            # Calculate balance as of yesterday
            balance = accounting_service.calculate_account_balance(
                account=account,
                as_of_date=yesterday,
                branch=None  # Calculate for all branches
            )
            
            # Determine debit and credit balances based on account type
            if account.account_type in ['asset', 'expense']:
                # Assets and expenses have debit balances (positive = debit)
                if balance >= Decimal('0.00'):
                    debit_balance = balance
                    credit_balance = Decimal('0.00')
                else:
                    debit_balance = Decimal('0.00')
                    credit_balance = abs(balance)
            else:  # liability, equity, income
                # Liabilities, equity, and income have credit balances (positive = credit)
                if balance >= Decimal('0.00'):
                    debit_balance = Decimal('0.00')
                    credit_balance = balance
                else:
                    debit_balance = abs(balance)
                    credit_balance = Decimal('0.00')
            
            # Create or update AccountBalance record
            account_balance, created = AccountBalance.objects.update_or_create(
                account=account,
                branch=None,  # NULL for all-branches aggregate
                as_of_date=yesterday,
                defaults={
                    'debit_balance': debit_balance,
                    'credit_balance': credit_balance,
                    'net_balance': balance,
                }
            )
            
            if created:
                created_count += 1
            else:
                updated_count += 1
            
            logger.debug(
                f"Cached balance for account {account.code}: {balance}"
            )
            
        except Exception as e:
            error_count += 1
            error_msg = (
                f"Error caching balance for account {account.code}: {str(e)}"
            )
            logger.error(error_msg, exc_info=True)
            errors.append(error_msg)
            # Continue processing other accounts
            continue
    
    # Log summary
    summary = {
        'date': str(yesterday),
        'processed': processed_count,
        'created': created_count,
        'updated': updated_count,
        'errors': error_count,
        'error_details': errors[:10]  # Limit to first 10 errors
    }
    
    logger.info(
        f"Completed account balance caching. "
        f"Processed: {processed_count}, "
        f"Created: {created_count}, "
        f"Updated: {updated_count}, "
        f"Errors: {error_count}"
    )
    
    return summary
