"""
FLUTTERWAVE SERVICE DOCUMENTATION

Location: shop/flutterwave_service.py

OVERVIEW
--------
This service provides a complete implementation for handling Flutterwave recurring
and installment payments in the NexusMart e-commerce platform.

MAIN CLASSES
============

1. FlutterwaveService
   Main service class for all payment operations

   ATTRIBUTES:
   - BASE_URL: Flutterwave API base URL
   - REQUEST_TIMEOUT: Default 30 seconds
   - MAX_RETRY_ATTEMPTS: 3 attempts before marking payment failed
   - RETRY_BACKOFF_MINUTES: [5, 15, 60] - Progressive delay between retries

   METHODS:
   
   a) initiate_payment(amount, email, phone, order_reference, customer_name="", currency="USD")
      Initialize payment to get authorization URL for card tokenization
      
      Usage:
        from shop.flutterwave_service import FlutterwaveService
        service = FlutterwaveService()
        result = service.initiate_payment(
            amount=Decimal("500.00"),
            email="customer@example.com",
            phone="+234801234567",
            order_reference="ORD123456",
            customer_name="John Doe"
        )
        if result['status'] == 'success':
            authorization_url = result['authorization_url']
            # Redirect customer to authorization_url
      
      Returns:
        {
            'status': 'success' | 'failed',
            'authorization_url': str,
            'access_code': str,
            'transaction_ref': str,
            'message': str
        }

   b) charge_recurring_card(amount, email, token, order_reference, installment_num, ...)
      Charge a saved card for installment payment
      
      Usage:
        result = service.charge_recurring_card(
            amount=Decimal("250.00"),
            email="customer@example.com",
            token="flw_t_saved_card_token_xyz",
            order_reference="ORD123456",
            installment_num=2
        )
        if result['status'] == 'success':
            transaction_ref = result['transaction_ref']
            # Mark payment as complete in ScheduledPayment model
      
      Returns:
        {
            'status': 'success' | 'failed',
            'transaction_ref': str,
            'message': str,
            'gateway_response': dict (optional)
        }

   c) verify_transaction(reference)
      Verify if a transaction was successful
      
      Usage:
        result = service.verify_transaction(reference="NX20240115120345ABC1")
        if result['verified']:
            print(f"Payment successful: {result['amount']}")
      
      Returns:
        {
            'status': 'success' | 'failed' | 'pending',
            'verified': bool,
            'amount': str,
            'charge_amount': str,
            'message': str,
            'gateway_response': dict
        }

   d) handle_failed_payment(payment_obj, error_msg, auto_retry=True)
      Handle payment failures with automatic retry logic
      
      Usage:
        from shop.models import ScheduledPayment
        payment = ScheduledPayment.objects.get(id=1)
        result = service.handle_failed_payment(
            payment_obj=payment,
            error_msg="Insufficient funds",
            auto_retry=True
        )
        if result['retry_scheduled']:
            print(f"Retry scheduled for: {result['next_retry_date']}")
      
      Returns:
        {
            'status': 'scheduled_retry' | 'max_retries_exceeded',
            'retry_scheduled': bool,
            'next_retry_date': datetime | None,
            'message': str
        }

   e) calculate_next_payment_date(start_date, payment_number, interval="monthly")
      Calculate the due date for the next payment
      
      Usage:
        next_date = service.calculate_next_payment_date(
            start_date=order.created_at,
            payment_number=2,
            interval="monthly"
        )
      
      Returns:
        datetime of the next payment due date

EXCEPTION CLASSES
=================

1. FlutterwaveServiceError (base exception)
   General service errors

2. FlutterwaveTimeoutError (extends FlutterwaveServiceError)
   API request timed out

3. FlutterwaveNetworkError (extends FlutterwaveServiceError)
   Network connection failed

CONFIGURATION
==============
Required Django settings (in nexusmart/settings.py):
- FLW_PUBLIC_KEY: Flutterwave public key
- FLW_SECRET_KEY: Flutterwave secret key
- FLW_ENCRYPTION_KEY: Flutterwave encryption key

These are loaded from environment variables:
- Set in .env file:
  FLW_PUBLIC_KEY=pk_test_xxxxx
  FLW_SECRET_KEY=sk_test_xxxxx
  FLW_ENCRYPTION_KEY=FLWSECK_xxxxx

INTEGRATION WITH MODELS
=======================

The service integrates with these models:
1. RecurringPaymentToken
   - Stores saved card tokens
   - field: flutterwave_token
   - field: authorization_url
   - field: is_authorized

2. InstallmentSchedule
   - Tracks monthly payment schedule
   - field: next_payment_date
   - field: auto_retry
   - field: max_retries

3. ScheduledPayment
   - Individual payment charge
   - field: transaction_reference
   - field: gateway_response
   - field: status
   - field: retry_count
   - field: error_message

4. Order
   - Parent order model
   - field: payment_type ('full' or 'installment')
   - field: total_installments
   - field: installments_paid
   - field: flutterwave_token
   - field: next_due_date

TYPICAL WORKFLOW
================

1. INITIAL PAYMENT & AUTHORIZATION:
   
   a) Customer initiates installment payment for order
   b) Call initiate_payment() to get authorization URL
   c) Redirect customer to authorization_url
   d) Customer authorizes their card
   e) Flutterwave returns authorization code
   f) Save token in RecurringPaymentToken model

2. SUBSEQUENT INSTALLMENTS:
   
   a) At scheduled due date, call charge_recurring_card()
   b) Pass saved token from RecurringPaymentToken
   c) If successful: mark ScheduledPayment as successful
   d) If failed: call handle_failed_payment() for retry logic
   e) Auto-retry according to backoff schedule
   f) After max retries: suspend installment schedule

3. PAYMENT VERIFICATION:
   
   a) Call verify_transaction() with transaction reference
   b) Check if payment actually went through
   c) Update payment status accordingly
   d) Log result for audit trail

ERROR HANDLING
==============

The service gracefully handles:
- Timeout errors: Raised as FlutterwaveTimeoutError
- Network errors: Raised as FlutterwaveNetworkError
- API errors: Logged and returned in response dict
- Invalid responses: Caught and logged

All methods return dict responses instead of raising exceptions for
business logic errors, allowing for proper handling in views/celery tasks.

LOGGING
=======

All operations are logged to Django's logger:
- Info logs: Successful operations
- Warning logs: Business logic issues (failed payments, max retries)
- Error logs: System/network issues, exceptions

Configure logging in Django settings to see service logs:
LOGGING = {
    'loggers': {
        'shop.flutterwave_service': {
            'level': 'DEBUG',
            'handlers': ['console', 'file'],
        }
    }
}

EXAMPLE IMPLEMENTATION IN VIEWS
================================

from decimal import Decimal
from shop.flutterwave_service import FlutterwaveService
from shop.models import Order, RecurringPaymentToken, InstallmentSchedule

service = FlutterwaveService()

# Step 1: Initiate payment for 3-month installment
def create_installment_payment(request, order_id):
    order = Order.objects.get(id=order_id)
    installment_amount = order.total / Decimal('3')
    
    result = service.initiate_payment(
        amount=installment_amount,
        email=request.user.email,
        phone=request.user.phone,
        order_reference=str(order.id),
        customer_name=request.user.get_full_name()
    )
    
    if result['status'] == 'success':
        return redirect(result['authorization_url'])
    else:
        return render(request, 'error.html', {'message': result['message']})

# Step 2: Process installment charge (background task)
from celery import shared_task

@shared_task
def charge_installment(scheduled_payment_id):
    payment = ScheduledPayment.objects.get(id=scheduled_payment_id)
    token = payment.installment_schedule.payment_token.flutterwave_token
    
    result = service.charge_recurring_card(
        amount=payment.amount,
        email=payment.order.user.email,
        token=token,
        order_reference=str(payment.order.id),
        installment_num=payment.installment_number
    )
    
    if result['status'] == 'success':
        payment.mark_as_successful(
            transaction_ref=result['transaction_ref'],
            gateway_response=result.get('gateway_response')
        )
        payment.installment_schedule.mark_payment_complete()
    else:
        retry_result = service.handle_failed_payment(
            payment_obj=payment,
            error_msg=result['message']
        )
        if retry_result['retry_scheduled']:
            # Schedule retry using celery
            charge_installment.apply_async(
                args=[payment.id],
                eta=retry_result['next_retry_date']
            )

TESTING
=======

For testing without real Flutterwave API:
1. Mock the _make_request() method
2. Use test credentials from Flutterwave sandbox
3. All responses are dicts, easy to mock

Example test:
    from unittest.mock import patch
    from decimal import Decimal
    
    with patch.object(FlutterwaveService, '_make_request') as mock_request:
        mock_request.return_value = {'status': 'success', 'data': {...}}
        service = FlutterwaveService()
        result = service.initiate_payment(...)
        assert result['status'] == 'success'

PRODUCTION DEPLOYMENT
=====================

1. Set production Flutterwave keys in environment:
   - FLW_PUBLIC_KEY=pk_live_xxxxx
   - FLW_SECRET_KEY=sk_live_xxxxx
   - FLW_ENCRYPTION_KEY=FLWSECK_live_xxxxx

2. Enable proper logging to files

3. Set up Celery for background payment processing

4. Configure webhooks for payment notifications

5. Set REQUEST_TIMEOUT appropriately (consider network conditions)

6. Monitor retry queue and manual intervention for suspended schedules

PERFORMANCE NOTES
==================

- All API calls use 30-second timeout to prevent hanging requests
- Consider implementing request caching for verify_transaction()
- Use Celery for background payment charging
- Database transactions should wrap payment record updates
"""
