"""
Subscription renewal tasks
Handles automatic subscription renewals with Celery
"""
from datetime import timedelta
from django.db import transaction
from django.utils import timezone
from celery import shared_task
from igny8_core.business.billing.models import Subscription, Invoice, Payment
from igny8_core.business.billing.services.invoice_service import InvoiceService
from igny8_core.business.billing.services.email_service import BillingEmailService
from igny8_core.business.billing.config import SUBSCRIPTION_RENEWAL_NOTICE_DAYS
import logging

logger = logging.getLogger(__name__)


@shared_task(name='billing.send_renewal_notices')
def send_renewal_notices():
    """
    Send renewal notice emails to subscribers
    Run daily to check subscriptions expiring soon
    """
    notice_date = timezone.now().date() + timedelta(days=SUBSCRIPTION_RENEWAL_NOTICE_DAYS)
    
    # Get active subscriptions expiring on notice_date
    subscriptions = Subscription.objects.filter(
        status='active',
        current_period_end__date=notice_date
    ).select_related('account', 'plan', 'account__owner')
    
    for subscription in subscriptions:
        # Check if notice already sent
        if subscription.metadata.get('renewal_notice_sent'):
            continue
        
        try:
            BillingEmailService.send_subscription_renewal_notice(
                subscription=subscription,
                days_until_renewal=SUBSCRIPTION_RENEWAL_NOTICE_DAYS
            )
            
            # Mark notice as sent
            subscription.metadata['renewal_notice_sent'] = True
            subscription.metadata['renewal_notice_sent_at'] = timezone.now().isoformat()
            subscription.save(update_fields=['metadata'])
            
            logger.info(f"Renewal notice sent for subscription {subscription.id}")
            
        except Exception as e:
            logger.exception(f"Failed to send renewal notice for subscription {subscription.id}: {str(e)}")


@shared_task(name='billing.process_subscription_renewals')
def process_subscription_renewals():
    """
    Process subscription renewals for subscriptions ending today
    Run daily at midnight to renew subscriptions
    """
    today = timezone.now().date()
    
    # Get active subscriptions ending today
    subscriptions = Subscription.objects.filter(
        status='active',
        current_period_end__date=today
    ).select_related('account', 'plan')
    
    logger.info(f"Processing {subscriptions.count()} subscription renewals for {today}")
    
    for subscription in subscriptions:
        try:
            renew_subscription(subscription.id)
        except Exception as e:
            logger.exception(f"Failed to renew subscription {subscription.id}: {str(e)}")


@shared_task(name='billing.renew_subscription')
def renew_subscription(subscription_id: int):
    """
    Renew a specific subscription
    
    Args:
        subscription_id: Subscription ID to renew
    """
    try:
        subscription = Subscription.objects.select_related('account', 'plan').get(id=subscription_id)
        
        if subscription.status != 'active':
            logger.warning(f"Subscription {subscription_id} is not active, skipping renewal")
            return
        
        with transaction.atomic():
            # Create renewal invoice
            invoice = InvoiceService.create_subscription_invoice(
                account=subscription.account,
                subscription=subscription
            )
            
            # Attempt automatic payment if payment method on file
            payment_attempted = False
            
            # Check if account has saved payment method
            if subscription.metadata.get('stripe_subscription_id'):
                payment_attempted = _attempt_stripe_renewal(subscription, invoice)
            elif subscription.metadata.get('paypal_subscription_id'):
                payment_attempted = _attempt_paypal_renewal(subscription, invoice)
            
            if payment_attempted:
                # Payment processing will handle subscription renewal
                logger.info(f"Automatic payment initiated for subscription {subscription_id}")
            else:
                # No automatic payment - send invoice for manual payment
                logger.info(f"Manual payment required for subscription {subscription_id}")
                
                # Mark subscription as pending renewal
                subscription.status = 'pending_renewal'
                subscription.metadata['renewal_invoice_id'] = invoice.id
                subscription.metadata['renewal_required_at'] = timezone.now().isoformat()
                subscription.save(update_fields=['status', 'metadata'])
                
                # TODO: Send invoice email
            
            # Clear renewal notice flag
            if 'renewal_notice_sent' in subscription.metadata:
                del subscription.metadata['renewal_notice_sent']
                subscription.save(update_fields=['metadata'])
                
    except Subscription.DoesNotExist:
        logger.error(f"Subscription {subscription_id} not found for renewal")
    except Exception as e:
        logger.exception(f"Error renewing subscription {subscription_id}: {str(e)}")


def _attempt_stripe_renewal(subscription: Subscription, invoice: Invoice) -> bool:
    """
    Attempt to charge Stripe subscription
    
    Returns:
        True if payment initiated, False otherwise
    """
    try:
        import stripe
        from igny8_core.business.billing.utils.payment_gateways import get_stripe_client
        
        stripe_client = get_stripe_client()
        
        # Retrieve Stripe subscription
        stripe_sub = stripe_client.Subscription.retrieve(
            subscription.metadata['stripe_subscription_id']
        )
        
        # Create payment intent for renewal
        intent = stripe_client.PaymentIntent.create(
            amount=int(float(invoice.total_amount) * 100),
            currency=invoice.currency.lower(),
            customer=stripe_sub.customer,
            payment_method=stripe_sub.default_payment_method,
            off_session=True,
            confirm=True,
            metadata={
                'invoice_id': invoice.id,
                'subscription_id': subscription.id
            }
        )
        
        # Create payment record
        Payment.objects.create(
            account=subscription.account,
            invoice=invoice,
            amount=invoice.total_amount,
            currency=invoice.currency,
            payment_method='stripe',
            status='processing',
            stripe_payment_intent_id=intent.id,
            metadata={'renewal': True}
        )
        
        return True
        
    except Exception as e:
        logger.exception(f"Stripe renewal payment failed for subscription {subscription.id}: {str(e)}")
        return False


def _attempt_paypal_renewal(subscription: Subscription, invoice: Invoice) -> bool:
    """
    Attempt to charge PayPal subscription
    
    Returns:
        True if payment initiated, False otherwise
    """
    try:
        from igny8_core.business.billing.utils.payment_gateways import get_paypal_client
        
        paypal_client = get_paypal_client()
        
        # PayPal subscriptions bill automatically
        # We just need to verify the subscription is still active
        paypal_sub = paypal_client.subscriptions.get(
            subscription.metadata['paypal_subscription_id']
        )
        
        if paypal_sub.status == 'ACTIVE':
            # PayPal will charge automatically, create payment record as processing
            Payment.objects.create(
                account=subscription.account,
                invoice=invoice,
                amount=invoice.total_amount,
                currency=invoice.currency,
                payment_method='paypal',
                status='processing',
                paypal_order_id=subscription.metadata['paypal_subscription_id'],
                metadata={'renewal': True}
            )
            return True
        else:
            logger.warning(f"PayPal subscription {paypal_sub.id} status: {paypal_sub.status}")
            return False
        
    except Exception as e:
        logger.exception(f"PayPal renewal check failed for subscription {subscription.id}: {str(e)}")
        return False
