"""
Management command to process scheduled installment payments
Run with: python manage.py process_installment_payments

This command:
1. Finds all pending payments that are due
2. Charges the customer's saved card
3. Handles failed payments with retry logic
4. Updates payment and installment schedule statuses
5. Sends email notifications to customers
"""

import logging
from datetime import timedelta
from decimal import Decimal
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.conf import settings

from shop.models import ScheduledPayment, InstallmentSchedule
from shop.flutterwave_service import FlutterwaveService

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    help = 'Process scheduled installment payments. Charges customer cards automatically.'
    
    def add_arguments(self, parser):
        parser.add_argument(
            '--dry-run',
            action='store_true',
            help='Show what would be processed without actually charging',
        )
        parser.add_argument(
            '--schedule-id',
            type=int,
            help='Process specific installment schedule ID',
        )
        parser.add_argument(
            '--order-id',
            type=int,
            help='Process specific order ID',
        )
    
    def handle(self, *args, **options):
        dry_run = options.get('dry_run', False)
        schedule_id = options.get('schedule_id')
        order_id = options.get('order_id')
        
        self.stdout.write("=" * 60)
        self.stdout.write("INSTALLMENT PAYMENT PROCESSOR")
        self.stdout.write(f"Mode: {'DRY RUN' if dry_run else 'LIVE'}")
        self.stdout.write("=" * 60)
        
        # Get payments to process
        query = ScheduledPayment.objects.filter(
            status__in=['pending', 'processing'],
            due_date__lte=timezone.now().date()
        )
        
        if schedule_id:
            query = query.filter(installment_schedule_id=schedule_id)
        if order_id:
            query = query.filter(order_id=order_id)
        
        payments_to_process = query.select_related(
            'installment_schedule',
            'installment_schedule__payment_token',
            'order',
            'order__user'
        ).order_by('due_date')
        
        if not payments_to_process.exists():
            self.stdout.write(self.style.SUCCESS("No payments to process"))
            return
        
        self.stdout.write(
            f"\nFound {payments_to_process.count()} payment(s) to process\n"
        )
        
        flutterwave_service = FlutterwaveService()
        processed_count = 0
        success_count = 0
        failure_count = 0
        
        for payment in payments_to_process:
            self.process_payment(
                payment,
                flutterwave_service,
                dry_run
            )
            processed_count += 1
            
            # Update counters
            if payment.status == 'successful':
                success_count += 1
            else:
                failure_count += 1
        
        # Summary
        self.stdout.write("\n" + "=" * 60)
        self.stdout.write("SUMMARY")
        self.stdout.write("=" * 60)
        self.stdout.write(f"Processed: {processed_count}")
        self.stdout.write(self.style.SUCCESS(f"Successful: {success_count}"))
        self.stdout.write(self.style.WARNING(f"Failed: {failure_count}"))
        self.stdout.write("=" * 60 + "\n")
    
    def process_payment(self, payment, flutterwave_service, dry_run=False):
        """
        Process a single scheduled payment
        """
        order = payment.order
        user = order.user
        schedule = payment.installment_schedule
        
        payment_info = (
            f"Payment #{payment.installment_number} | "
            f"Order #{order.id} | "
            f"Customer: {user.email} | "
            f"Amount: ${payment.amount:.2f}"
        )
        
        self.stdout.write(f"\nProcessing: {payment_info}")
        
        # Validate payment setup
        if not schedule.payment_token:
            error_msg = "No payment token available"
            self.stdout.write(self.style.ERROR(f"  ✗ {error_msg}"))
            self.mark_payment_failed(payment, error_msg, dry_run)
            return
        
        if not schedule.payment_token.is_authorized:
            error_msg = "Payment token not authorized"
            self.stdout.write(self.style.ERROR(f"  ✗ {error_msg}"))
            self.mark_payment_failed(payment, error_msg, dry_run)
            return
        
        if not schedule.payment_token.is_active:
            error_msg = "Payment token is inactive"
            self.stdout.write(self.style.ERROR(f"  ✗ {error_msg}"))
            self.mark_payment_failed(payment, error_msg, dry_run)
            return
        
        if dry_run:
            self.stdout.write(self.style.SUCCESS(f"  ✓ Would charge ${payment.amount:.2f}"))
            return
        
        # Mark as processing
        payment.status = 'processing'
        payment.save()
        
        try:
            # Attempt to charge the card
            self.stdout.write("  Charging card...", ending=" ")
            
            charge_response = flutterwave_service.charge_recurring_card(
                amount=payment.amount,
                email=user.email,
                token=schedule.payment_token.flutterwave_token,
                order_reference=f"INSTALL-{order.id}-{payment.installment_number}",
                installment_num=payment.installment_number,
                customer_name=user.get_full_name() or user.username,
            )
            
            if charge_response['status'] == 'success':
                # Payment successful
                payment.mark_as_successful(
                    transaction_ref=charge_response['transaction_ref'],
                    gateway_response=charge_response.get('gateway_response', {})
                )
                
                # Update schedule
                schedule.paid_installments += 1
                schedule.failed_attempts = 0
                
                if schedule.is_complete:
                    schedule.status = 'completed'
                else:
                    # Update next payment date
                    from dateutil.relativedelta import relativedelta
                    schedule.next_payment_date = schedule.next_payment_date + relativedelta(months=1)
                
                schedule.save()
                
                # Update order
                order.installments_paid = schedule.paid_installments
                order.save()
                
                self.stdout.write(self.style.SUCCESS("✓ Success"))
                self.send_payment_success_email(payment)
                
                logger.info(
                    f"Successfully charged {user.email} "
                    f"${payment.amount:.2f} for installment {payment.installment_number}"
                )
                
            else:
                # Charge failed
                error_msg = charge_response.get('message', 'Charge declined')
                self.stdout.write(self.style.ERROR(f"✗ Failed: {error_msg}"))
                self.mark_payment_failed(payment, error_msg, dry_run=False)
                self.send_payment_failure_email(payment, error_msg)
                
                logger.warning(
                    f"Failed to charge {user.email} "
                    f"for installment {payment.installment_number}: {error_msg}"
                )
        
        except Exception as e:
            error_msg = str(e)
            self.stdout.write(self.style.ERROR(f"✗ Error: {error_msg}"))
            self.mark_payment_failed(payment, error_msg, dry_run=False)
            self.send_payment_failure_email(payment, error_msg)
            
            logger.exception(
                f"Exception charging {user.email} "
                f"for installment {payment.installment_number}"
            )
    
    def mark_payment_failed(self, payment, error_msg, dry_run=False):
        """
        Mark a payment as failed and handle retry logic
        """
        if dry_run:
            return
        
        schedule = payment.installment_schedule
        flutterwave_service = FlutterwaveService()
        
        # Mark payment as failed
        payment.mark_as_failed(error_msg)
        
        # Attempt auto-retry if enabled
        if schedule.auto_retry and payment.retry_count < schedule.max_retries:
            # Update next payment date for retry
            payment.due_date = (timezone.now() + timedelta(hours=4)).date()
            payment.save()
            
            self.stdout.write(
                f"  Will retry in 4 hours (Attempt {payment.retry_count}/{schedule.max_retries})"
            )
        else:
            # No more retries - suspend schedule
            schedule.failed_attempts += 1
            
            if schedule.auto_retry and payment.retry_count >= schedule.max_retries:
                schedule.status = 'suspended'
                schedule.save()
                
                self.stdout.write(
                    self.style.ERROR(
                        "  Max retries exceeded - Schedule suspended"
                    )
                )
                
                # Notify customer
                self.send_payment_suspended_email(payment)
    
    def send_payment_success_email(self, payment):
        """
        Send success email to customer
        """
        try:
            order = payment.order
            user = order.user
            schedule = payment.installment_schedule
            
            context = {
                'user': user,
                'order': order,
                'payment': payment,
                'schedule': schedule,
                'remaining': schedule.remaining_installments,
                'next_date': schedule.next_payment_date,
            }
            
            subject = f"Payment Successful - Order #{order.id} (Installment {payment.installment_number})"
            message = render_to_string(
                'shop/emails/payment_success.html',
                context
            )
            
            send_mail(
                subject,
                message,
                settings.DEFAULT_FROM_EMAIL,
                [user.email],
                html_message=message,
                fail_silently=True,
            )
        except Exception as e:
            logger.error(f"Error sending payment success email: {e}")
    
    def send_payment_failure_email(self, payment, error_msg):
        """
        Send failure notification to customer
        """
        try:
            order = payment.order
            user = order.user
            schedule = payment.installment_schedule
            
            context = {
                'user': user,
                'order': order,
                'payment': payment,
                'schedule': schedule,
                'error_message': error_msg,
                'retry_count': payment.retry_count,
                'max_retries': schedule.max_retries,
            }
            
            subject = f"Payment Failed - Order #{order.id} - Action Required"
            message = render_to_string(
                'shop/emails/payment_failure.html',
                context
            )
            
            send_mail(
                subject,
                message,
                settings.DEFAULT_FROM_EMAIL,
                [user.email],
                html_message=message,
                fail_silently=True,
            )
        except Exception as e:
            logger.error(f"Error sending payment failure email: {e}")
    
    def send_payment_suspended_email(self, payment):
        """
        Send suspension notice to customer (max retries exceeded)
        """
        try:
            order = payment.order
            user = order.user
            schedule = payment.installment_schedule
            
            context = {
                'user': user,
                'order': order,
                'payment': payment,
                'schedule': schedule,
            }
            
            subject = f"Payment Schedule Suspended - Order #{order.id} - Update Payment Method"
            message = render_to_string(
                'shop/emails/payment_suspended.html',
                context
            )
            
            send_mail(
                subject,
                message,
                settings.DEFAULT_FROM_EMAIL,
                [user.email],
                html_message=message,
                fail_silently=True,
            )
        except Exception as e:
            logger.error(f"Error sending payment suspended email: {e}")
