"""
Example Integration: Flutterwave Recurring Payment Views & Tasks

This file demonstrates how to integrate the FlutterwaveService into
Django views and Celery tasks. Copy and adapt these examples to your
specific implementation.
"""

from decimal import Decimal
from django.shortcuts import render, redirect
from django.views import View
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from django.utils import timezone
from django.utils.decorators import method_decorator
from dateutil.relativedelta import relativedelta

from shop.models import (
    Order, OrderItem, Product,
    RecurringPaymentToken, InstallmentSchedule, ScheduledPayment
)
from shop.flutterwave_service import FlutterwaveService


# ============================================================================
# DJANGO VIEWS EXAMPLES
# ============================================================================

@method_decorator(login_required, name='dispatch')
class CreateInstallmentOrderView(View):
    """Create an order with installment payment plan"""
    
    def post(self, request):
        """
        Create order with installment payment
        
        POST data:
        - product_ids: List of product IDs
        - months: Number of installments (3, 6, etc.)
        """
        try:
            product_ids = request.POST.getlist('product_ids')
            months = int(request.POST.get('months', 3))
            
            # Fetch products
            products = Product.objects.filter(id__in=product_ids)
            total = sum(p.price for p in products)
            
            # Create order
            order = Order.objects.create(
                user=request.user,
                total=total,
                phone=request.user.profile.phone or request.POST.get('phone'),
                payment_type='installment',
                total_installments=months,
                status='Pending'
            )
            
            # Add items
            for product in products:
                OrderItem.objects.create(
                    order=order,
                    product=product,
                    quantity=1,
                    price=product.price
                )
            
            # Redirect to payment authorization
            return redirect('initiate_installment_payment', order_id=order.id)
            
        except Exception as e:
            return render(request, 'error.html', {'error': str(e)})


@method_decorator(login_required, name='dispatch')
class InitiateInstallmentPaymentView(View):
    """Initiate payment for first installment"""
    
    def get(self, request, order_id):
        """Display payment initiation page"""
        try:
            order = Order.objects.get(id=order_id, user=request.user)
            installment_amount = order.total / Decimal(order.total_installments)
            
            context = {
                'order': order,
                'installment_amount': installment_amount,
                'total_installments': order.total_installments
            }
            return render(request, 'payment/initiate_installment.html', context)
            
        except Order.DoesNotExist:
            return render(request, 'error.html', {'error': 'Order not found'})
    
    def post(self, request, order_id):
        """Initiate payment with Flutterwave"""
        try:
            order = Order.objects.get(id=order_id, user=request.user)
            service = FlutterwaveService()
            
            installment_amount = order.total / Decimal(order.total_installments)
            
            # Initiate payment
            result = service.initiate_payment(
                amount=installment_amount,
                email=request.user.email,
                phone=request.user.profile.phone,
                order_reference=str(order.id),
                customer_name=request.user.get_full_name()
            )
            
            if result['status'] == 'success':
                # Store transaction ref in session for webhook verification
                request.session['flw_tx_ref'] = result['transaction_ref']
                request.session['order_id'] = order.id
                
                # Redirect to Flutterwave
                return redirect(result['authorization_url'])
            else:
                return render(request, 'error.html', {
                    'error': f"Payment initiation failed: {result['message']}"
                })
                
        except Order.DoesNotExist:
            return render(request, 'error.html', {'error': 'Order not found'})
        except Exception as e:
            return render(request, 'error.html', {'error': str(e)})


@login_required
def payment_callback_view(request):
    """
    Handle payment callback from Flutterwave
    
    URL parameter: ?status=successful&tx_ref=NX20240115120345ABC1
    """
    try:
        status = request.GET.get('status')
        tx_ref = request.GET.get('tx_ref')
        order_id = request.session.get('order_id')
        
        if not order_id:
            return render(request, 'error.html', {'error': 'Session expired'})
        
        order = Order.objects.get(id=order_id, user=request.user)
        service = FlutterwaveService()
        
        # Verify transaction with Flutterwave
        verify_result = service.verify_transaction(tx_ref)
        
        if verify_result['verified']:
            # Extract token from Flutterwave response
            # This would typically come from the authorization response
            token = request.GET.get('token')  # In real flow, get from Flutterwave
            
            # Save token for future charges
            payment_token, created = RecurringPaymentToken.objects.update_or_create(
                user=request.user,
                defaults={
                    'flutterwave_token': token,
                    'card_last_four': request.GET.get('card_last_four', ''),
                    'card_brand': request.GET.get('card_brand', 'Visa'),
                    'is_authorized': True,
                    'authorized_at': timezone.now(),
                    'gateway': 'flutterwave'
                }
            )
            
            # Create installment schedule
            create_installment_schedule(order, payment_token)
            
            # Update order status
            order.status = 'Processing'
            order.flutterwave_token = token
            order.save()
            
            return render(request, 'payment/payment_success.html', {
                'order': order,
                'message': 'Payment authorized successfully. Installments scheduled.'
            })
        else:
            order.status = 'Cancelled'
            order.save()
            
            return render(request, 'error.html', {
                'error': f"Payment verification failed: {verify_result['message']}"
            })
            
    except Order.DoesNotExist:
        return render(request, 'error.html', {'error': 'Order not found'})
    except Exception as e:
        return render(request, 'error.html', {'error': str(e)})


def create_installment_schedule(order, payment_token):
    """Helper function to create installment schedule and payments"""
    installment_amount = order.total / Decimal(order.total_installments)
    
    # Create schedule
    schedule = InstallmentSchedule.objects.create(
        order=order,
        payment_token=payment_token,
        installment_amount=installment_amount,
        total_installments=order.total_installments,
        next_payment_date=(timezone.now() + relativedelta(months=1)).date(),
        final_payment_date=(timezone.now() + relativedelta(months=order.total_installments)).date(),
        auto_retry=True,
        max_retries=3
    )
    
    # Create individual scheduled payments
    for i in range(1, order.total_installments + 1):
        due_date = timezone.now().date() + relativedelta(months=i)
        ScheduledPayment.objects.create(
            installment_schedule=schedule,
            order=order,
            amount=installment_amount,
            installment_number=i,
            scheduled_date=timezone.now().date(),
            due_date=due_date,
            status='pending'
        )


# ============================================================================
# CELERY TASKS EXAMPLES
# ============================================================================

try:
    from celery import shared_task
except ImportError:
    # If Celery not installed, create dummy decorator
    def shared_task(func):
        return func


@shared_task
def process_scheduled_payments():
    """
    Periodic task: Process all due installment payments
    
    Schedule: Run daily (e.g., every morning at 2 AM)
    
    Add to celery beat schedule:
    CELERY_BEAT_SCHEDULE = {
        'process-scheduled-payments': {
            'task': 'shop.tasks.process_scheduled_payments',
            'schedule': crontab(hour=2, minute=0),  # 2 AM daily
        },
    }
    """
    from django.utils import timezone
    
    # Find all pending payments due today or overdue
    today = timezone.now().date()
    pending_payments = ScheduledPayment.objects.filter(
        status='pending',
        due_date__lte=today
    )
    
    for payment in pending_payments:
        # Process each payment asynchronously
        charge_installment.apply_async(args=[payment.id])


@shared_task
def charge_installment(payment_id):
    """
    Charge a scheduled installment
    
    Called by process_scheduled_payments task
    """
    try:
        payment = ScheduledPayment.objects.get(id=payment_id)
        schedule = payment.installment_schedule
        token_obj = schedule.payment_token
        
        if not token_obj or not token_obj.is_authorized:
            payment.mark_as_failed("No authorized payment token")
            return
        
        service = FlutterwaveService()
        
        # Charge the card
        result = service.charge_recurring_card(
            amount=payment.amount,
            email=payment.order.user.email,
            token=token_obj.flutterwave_token,
            order_reference=str(payment.order.id),
            installment_num=payment.installment_number,
            customer_name=payment.order.user.get_full_name()
        )
        
        if result['status'] == 'success':
            # Mark payment as successful
            payment.mark_as_successful(
                transaction_ref=result['transaction_ref'],
                gateway_response=result.get('gateway_response', {})
            )
            
            # Update schedule
            schedule.mark_payment_complete()
            
            # Update order status if all payments done
            if schedule.is_complete:
                payment.order.status = 'Paid'
                payment.order.installments_paid = schedule.total_installments
                payment.order.save()
            
        else:
            # Handle failure with retry logic
            retry_result = service.handle_failed_payment(
                payment_obj=payment,
                error_msg=result['message'],
                auto_retry=True
            )
            
            if retry_result['retry_scheduled']:
                # Schedule retry
                charge_installment.apply_async(
                    args=[payment.id],
                    eta=retry_result['next_retry_date']
                )
            else:
                # Notify user of failed payment after max retries
                send_payment_failed_notification(payment)
                
    except ScheduledPayment.DoesNotExist:
        pass  # Payment already deleted
    except Exception as e:
        # Log error but don't fail task
        import logging
        logger = logging.getLogger('shop.payment_tasks')
        logger.error(f"Error charging installment {payment_id}: {str(e)}")


@shared_task
def verify_pending_transactions():
    """
    Periodic task: Verify status of pending/processing transactions
    
    Schedule: Every 30 minutes
    
    Add to celery beat schedule:
    CELERY_BEAT_SCHEDULE = {
        'verify-pending-transactions': {
            'task': 'shop.tasks.verify_pending_transactions',
            'schedule': timedelta(minutes=30),
        },
    }
    """
    from django.utils import timezone
    
    # Find processing payments that haven't been verified recently
    cutoff_time = timezone.now() - timezone.timedelta(hours=1)
    processing_payments = ScheduledPayment.objects.filter(
        status='processing',
        updated_at__lt=cutoff_time
    )
    
    service = FlutterwaveService()
    
    for payment in processing_payments:
        if payment.transaction_reference:
            # Verify transaction
            result = service.verify_transaction(payment.transaction_reference)
            
            if result['verified']:
                payment.mark_as_successful(
                    transaction_ref=payment.transaction_reference,
                    gateway_response=result.get('gateway_response', {})
                )
                payment.installment_schedule.mark_payment_complete()
            elif result['status'] == 'failed':
                retry_result = service.handle_failed_payment(
                    payment_obj=payment,
                    error_msg=result['message']
                )
                if retry_result['retry_scheduled']:
                    charge_installment.apply_async(
                        args=[payment.id],
                        eta=retry_result['next_retry_date']
                    )


@shared_task
def retry_failed_payments():
    """
    Periodic task: Retry failed payments
    
    Schedule: Every 6 hours
    
    Add to celery beat schedule:
    CELERY_BEAT_SCHEDULE = {
        'retry-failed-payments': {
            'task': 'shop.tasks.retry_failed_payments',
            'schedule': timedelta(hours=6),
        },
    }
    """
    # Find failed payments ready for retry
    failed_payments = ScheduledPayment.objects.filter(
        status='failed',
        retry_count__lt=3,
        last_retry_at__isnull=True  # Not yet retried
    )
    
    for payment in failed_payments[:100]:  # Process in batches
        charge_installment.apply_async(args=[payment.id])


def send_payment_failed_notification(payment):
    """Send email notification for failed payment"""
    from django.core.mail import send_mail
    from django.conf import settings
    
    user = payment.order.user
    order = payment.order
    
    subject = f"Payment Failed for Order #{order.id}"
    message = f"""
    Dear {user.first_name},
    
    The installment payment #{payment.installment_number} of ${payment.amount} 
    for order #{order.id} has failed after multiple retry attempts.
    
    Please update your payment method or contact support for assistance.
    
    Best regards,
    NexusMart Team
    """
    
    try:
        send_mail(
            subject,
            message,
            settings.DEFAULT_FROM_EMAIL,
            [user.email],
            fail_silently=False
        )
    except Exception as e:
        import logging
        logger = logging.getLogger('shop.payment_tasks')
        logger.error(f"Failed to send payment notification: {str(e)}")


# ============================================================================
# URL CONFIGURATION EXAMPLE
# ============================================================================

"""
Add to shop/urls.py:

from .views import (
    CreateInstallmentOrderView,
    InitiateInstallmentPaymentView,
    payment_callback_view
)

urlpatterns = [
    # ... existing patterns ...
    
    # Installment payment flows
    path('order/installment/create/', 
         CreateInstallmentOrderView.as_view(), 
         name='create_installment_order'),
    path('order/<int:order_id>/installment/pay/', 
         InitiateInstallmentPaymentView.as_view(), 
         name='initiate_installment_payment'),
    path('payment/callback/', 
         payment_callback_view, 
         name='payment_callback'),
]
"""

# ============================================================================
# CELERY CONFIGURATION EXAMPLE
# ============================================================================

"""
Add to your celery config (celery.py or settings.py):

from celery.schedules import crontab
from datetime import timedelta

CELERY_BEAT_SCHEDULE = {
    'process-scheduled-payments': {
        'task': 'shop.tasks.process_scheduled_payments',
        'schedule': crontab(hour=2, minute=0),  # Daily at 2 AM
    },
    'verify-pending-transactions': {
        'task': 'shop.tasks.verify_pending_transactions',
        'schedule': timedelta(minutes=30),  # Every 30 minutes
    },
    'retry-failed-payments': {
        'task': 'shop.tasks.retry_failed_payments',
        'schedule': timedelta(hours=6),  # Every 6 hours
    },
}
"""
