"""
Payment retry mechanism for failed payments
Implements automatic retry logic with exponential backoff
"""
from datetime import timedelta
from django.utils import timezone
from celery import shared_task
from igny8_core.business.billing.models import Payment
from igny8_core.business.billing.config import MAX_PAYMENT_RETRIES
import logging

logger = logging.getLogger(__name__)


@shared_task(name='billing.retry_failed_payment')
def retry_failed_payment(payment_id: int):
    """
    Retry a failed payment with exponential backoff
    
    Args:
        payment_id: Payment ID to retry
    """
    try:
        payment = Payment.objects.get(id=payment_id)
        
        # Only retry failed payments
        if payment.status != 'failed':
            logger.info(f"Payment {payment_id} status is {payment.status}, skipping retry")
            return
        
        # Check retry count
        retry_count = payment.metadata.get('retry_count', 0)
        if retry_count >= MAX_PAYMENT_RETRIES:
            logger.warning(f"Payment {payment_id} exceeded max retries ({MAX_PAYMENT_RETRIES})")
            payment.metadata['retry_exhausted'] = True
            payment.save(update_fields=['metadata'])
            return
        
        # Process retry based on payment method
        if payment.payment_method == 'stripe':
            success = _retry_stripe_payment(payment)
        elif payment.payment_method == 'paypal':
            success = _retry_paypal_payment(payment)
        else:
            # Manual payments cannot be automatically retried
            logger.info(f"Payment {payment_id} is manual, cannot auto-retry")
            return
        
        # Update retry count
        retry_count += 1
        payment.metadata['retry_count'] = retry_count
        payment.metadata['last_retry_at'] = timezone.now().isoformat()
        
        if success:
            payment.status = 'succeeded'
            payment.processed_at = timezone.now()
            payment.failure_reason = ''
            logger.info(f"Payment {payment_id} retry succeeded")
        else:
            # Schedule next retry with exponential backoff
            if retry_count < MAX_PAYMENT_RETRIES:
                delay_minutes = 5 * (2 ** retry_count)  # 5, 10, 20 minutes
                retry_failed_payment.apply_async(
                    args=[payment_id],
                    countdown=delay_minutes * 60
                )
                payment.metadata['next_retry_at'] = (
                    timezone.now() + timedelta(minutes=delay_minutes)
                ).isoformat()
                logger.info(f"Payment {payment_id} retry {retry_count} failed, next retry in {delay_minutes}m")
        
        payment.save(update_fields=['status', 'processed_at', 'failure_reason', 'metadata'])
        
    except Payment.DoesNotExist:
        logger.error(f"Payment {payment_id} not found for retry")
    except Exception as e:
        logger.exception(f"Error retrying payment {payment_id}: {str(e)}")


def _retry_stripe_payment(payment: Payment) -> bool:
    """
    Retry Stripe payment
    
    Args:
        payment: Payment instance
        
    Returns:
        True if retry succeeded, False otherwise
    """
    try:
        import stripe
        from igny8_core.business.billing.utils.payment_gateways import get_stripe_client
        
        stripe_client = get_stripe_client()
        
        # Retrieve payment intent
        intent = stripe_client.PaymentIntent.retrieve(payment.stripe_payment_intent_id)
        
        # Attempt to confirm the payment intent
        if intent.status == 'requires_payment_method':
            # Cannot retry without new payment method
            payment.failure_reason = 'Requires new payment method'
            return False
        elif intent.status == 'requires_action':
            # Requires customer action (3D Secure)
            payment.failure_reason = 'Requires customer authentication'
            return False
        elif intent.status == 'succeeded':
            return True
        
        return False
        
    except Exception as e:
        logger.exception(f"Stripe retry error for payment {payment.id}: {str(e)}")
        payment.failure_reason = str(e)
        return False


def _retry_paypal_payment(payment: Payment) -> bool:
    """
    Retry PayPal payment
    
    Args:
        payment: Payment instance
        
    Returns:
        True if retry succeeded, False otherwise
    """
    try:
        from igny8_core.business.billing.utils.payment_gateways import get_paypal_client
        
        paypal_client = get_paypal_client()
        
        # Check order status
        order = paypal_client.orders.get(payment.paypal_order_id)
        
        if order.status == 'APPROVED':
            # Attempt to capture
            capture_response = paypal_client.orders.capture(payment.paypal_order_id)
            if capture_response.status == 'COMPLETED':
                payment.paypal_capture_id = capture_response.purchase_units[0].payments.captures[0].id
                return True
        elif order.status == 'COMPLETED':
            return True
        
        payment.failure_reason = f'PayPal order status: {order.status}'
        return False
        
    except Exception as e:
        logger.exception(f"PayPal retry error for payment {payment.id}: {str(e)}")
        payment.failure_reason = str(e)
        return False


@shared_task(name='billing.schedule_payment_retries')
def schedule_payment_retries():
    """
    Periodic task to identify failed payments and schedule retries
    Should be run every 5 minutes via Celery Beat
    """
    # Get failed payments from last 24 hours that haven't exhausted retries
    cutoff_time = timezone.now() - timedelta(hours=24)
    
    failed_payments = Payment.objects.filter(
        status='failed',
        failed_at__gte=cutoff_time,
        payment_method__in=['stripe', 'paypal']  # Only auto-retry gateway payments
    ).exclude(
        metadata__has_key='retry_exhausted'
    )
    
    for payment in failed_payments:
        retry_count = payment.metadata.get('retry_count', 0)
        if retry_count < MAX_PAYMENT_RETRIES:
            # Schedule immediate retry if not already scheduled
            if 'next_retry_at' not in payment.metadata:
                retry_failed_payment.delay(payment.id)
                logger.info(f"Scheduled retry for payment {payment.id}")
