"""
Refund workflow for payments
Handles full and partial refunds with proper accounting
"""
from decimal import Decimal
from django.db import transaction
from django.utils import timezone
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from igny8_core.business.billing.models import Payment, CreditTransaction, Invoice
from igny8_core.business.billing.utils.errors import (
    error_response, success_response, not_found_response, ErrorCode
)
from igny8_core.business.billing.services.email_service import BillingEmailService
import logging

logger = logging.getLogger(__name__)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def initiate_refund(request, payment_id):
    """
    Initiate a refund for a payment
    
    Request body:
        {
            "amount": "50.00",  # Optional, defaults to full refund
            "reason": "Customer requested refund",
            "refund_credits": true  # Whether to deduct credits
        }
    """
    try:
        payment = Payment.objects.select_related('invoice', 'account').get(
            id=payment_id,
            account=request.user.account
        )
    except Payment.DoesNotExist:
        return not_found_response('Payment', payment_id)
    
    # Validate payment can be refunded
    if payment.status != 'succeeded':
        return error_response(
            message='Only successful payments can be refunded',
            code=ErrorCode.INVALID_STATUS_TRANSITION,
            status_code=status.HTTP_400_BAD_REQUEST
        )
    
    if payment.refunded_at:
        return error_response(
            message='This payment has already been refunded',
            code=ErrorCode.PAYMENT_ALREADY_PROCESSED,
            status_code=status.HTTP_400_BAD_REQUEST
        )
    
    # Parse refund amount
    refund_amount = request.data.get('amount')
    if refund_amount:
        refund_amount = Decimal(str(refund_amount))
        if refund_amount > Decimal(payment.amount):
            return error_response(
                message=f'Refund amount cannot exceed payment amount ({payment.amount})',
                code=ErrorCode.VALIDATION_ERROR,
                status_code=status.HTTP_400_BAD_REQUEST
            )
    else:
        refund_amount = Decimal(payment.amount)
    
    reason = request.data.get('reason', 'Refund requested')
    refund_credits = request.data.get('refund_credits', True)
    
    try:
        with transaction.atomic():
            # Process refund based on payment method
            refund_successful = False
            
            if payment.payment_method == 'stripe':
                refund_successful = _process_stripe_refund(payment, refund_amount, reason)
            elif payment.payment_method == 'paypal':
                refund_successful = _process_paypal_refund(payment, refund_amount, reason)
            else:
                # Manual payment refund - mark as refunded
                refund_successful = True
            
            if not refund_successful:
                return error_response(
                    message='Refund processing failed. Please contact support.',
                    code=ErrorCode.INTERNAL_ERROR,
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
                )
            
            # Update payment record
            payment.status = 'refunded'
            payment.refunded_at = timezone.now()
            payment.metadata['refund_amount'] = str(refund_amount)
            payment.metadata['refund_reason'] = reason
            payment.save(update_fields=['status', 'refunded_at', 'metadata'])
            
            # Update invoice if full refund
            if refund_amount == Decimal(payment.amount):
                invoice = payment.invoice
                if invoice.status == 'paid':
                    invoice.status = 'pending'
                    invoice.paid_at = None
                    invoice.save(update_fields=['status', 'paid_at'])
            
            # Deduct credits if applicable
            if refund_credits and payment.credit_transactions.exists():
                for credit_tx in payment.credit_transactions.all():
                    if credit_tx.amount > 0:  # Only deduct positive credits
                        # Get current balance
                        account = payment.account
                        current_balance = account.credit_balance
                        
                        # Create deduction transaction
                        CreditTransaction.objects.create(
                            account=account,
                            transaction_type='refund',
                            amount=-credit_tx.amount,
                            balance_after=current_balance - credit_tx.amount,
                            description=f'Refund: {reason}',
                            payment=payment,
                            metadata={'original_transaction': credit_tx.id}
                        )
                        
                        # Update account balance
                        account.credit_balance -= credit_tx.amount
                        account.save(update_fields=['credit_balance'])
            
            # Send refund notification email
            try:
                BillingEmailService.send_refund_notification(
                    user=payment.account.owner,
                    payment=payment,
                    refund_amount=str(refund_amount),
                    reason=reason
                )
            except Exception as e:
                logger.error(f"Failed to send refund email for payment {payment_id}: {str(e)}")
            
            return success_response(
                data={
                    'payment_id': payment.id,
                    'refund_amount': str(refund_amount),
                    'status': 'refunded'
                },
                message='Refund processed successfully'
            )
            
    except Exception as e:
        logger.exception(f"Refund error for payment {payment_id}: {str(e)}")
        return error_response(
            message='An error occurred while processing the refund',
            code=ErrorCode.INTERNAL_ERROR,
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


def _process_stripe_refund(payment: Payment, amount: Decimal, reason: str) -> bool:
    """Process Stripe refund"""
    try:
        import stripe
        from igny8_core.business.billing.utils.payment_gateways import get_stripe_client
        
        stripe_client = get_stripe_client()
        
        refund = stripe_client.Refund.create(
            payment_intent=payment.stripe_payment_intent_id,
            amount=int(amount * 100),  # Convert to cents
            reason='requested_by_customer',
            metadata={'reason': reason}
        )
        
        payment.metadata['stripe_refund_id'] = refund.id
        return refund.status == 'succeeded'
        
    except Exception as e:
        logger.exception(f"Stripe refund failed for payment {payment.id}: {str(e)}")
        return False


def _process_paypal_refund(payment: Payment, amount: Decimal, reason: str) -> bool:
    """Process PayPal refund"""
    try:
        from igny8_core.business.billing.utils.payment_gateways import get_paypal_client
        
        paypal_client = get_paypal_client()
        
        refund_request = {
            'amount': {
                'value': str(amount),
                'currency_code': payment.currency
            },
            'note_to_payer': reason
        }
        
        refund = paypal_client.payments.captures.refund(
            payment.paypal_capture_id,
            refund_request
        )
        
        payment.metadata['paypal_refund_id'] = refund.id
        return refund.status == 'COMPLETED'
        
    except Exception as e:
        logger.exception(f"PayPal refund failed for payment {payment.id}: {str(e)}")
        return False
