"""
Payments Services - Stripe Connect Integration

CRITICAL SECURITY REQUIREMENTS:
1. ALL Stripe API calls MUST include stripe_account=tenant.stripe_connect_id header
2. Application fees MUST be calculated and passed to ensure platform revenue
3. Tenant is the Merchant of Record, not the platform
"""
import stripe
from django.conf import settings
from decimal import Decimal
from .models import TransactionLink


class StripeService:
    """
    Service for Stripe Connect Standard account operations.
    
    The Golden Rule: Every Stripe API call must include:
    stripe_account=tenant.stripe_connect_id
    
    This ensures the tenant is the Merchant of Record and
    payments flow through their connected account.
    """
    
    def __init__(self, tenant):
        """
        Initialize with tenant context.
        
        Args:
            tenant: core.Tenant instance with stripe_connect_id
        """
        self.tenant = tenant
        stripe.api_key = settings.STRIPE_SECRET_KEY
    
    def create_payment_intent(
        self,
        event,
        amount,
        payment_method_id=None,
        locale='en',
        application_fee_percent=Decimal('5.0')
    ):
        """
        Create a Stripe PaymentIntent for an event.
        
        CRITICAL: Uses stripe_account header to route payment to tenant.
        
        Args:
            event: schedule.Event instance
            amount: Decimal amount in tenant currency
            payment_method_id: Optional Stripe PaymentMethod ID
            locale: Language code for receipts (en/es/fr/de)
            application_fee_percent: Platform fee percentage (default 5%)
        
        Returns:
            tuple: (PaymentIntent object, TransactionLink instance)
        
        Raises:
            stripe.error.StripeError: On API errors
        """
        # Calculate application fee (platform revenue)
        application_fee_amount = (amount * application_fee_percent / 100).quantize(
            Decimal('0.01')
        )
        
        # Convert to cents for Stripe API
        amount_cents = int(amount * 100)
        fee_cents = int(application_fee_amount * 100)
        
        # CRITICAL: Create PaymentIntent with stripe_account header
        payment_intent = stripe.PaymentIntent.create(
            amount=amount_cents,
            currency=self.tenant.currency.lower() if hasattr(self.tenant, 'currency') else 'usd',
            payment_method=payment_method_id,
            application_fee_amount=fee_cents,  # CRITICAL: Platform fee
            stripe_account=self.tenant.stripe_connect_id,  # CRITICAL: Tenant as merchant
            metadata={
                'locale': locale,
                'event_id': event.id,
                'event_title': event.title,
                'tenant_id': self.tenant.id,
                'tenant_name': self.tenant.name,
            },
            confirmation_method='manual',  # Require explicit confirmation
            capture_method='automatic',
        )
        
        # Create tenant-scoped transaction record
        transaction = TransactionLink.objects.create(
            event=event,
            payment_intent_id=payment_intent.id,
            amount=amount,
            application_fee_amount=application_fee_amount,
            currency=payment_intent.currency.upper(),
            status=TransactionLink.Status.PENDING,
            payment_method_id=payment_method_id or '',
            locale=locale,
        )
        
        return payment_intent, transaction
    
    def confirm_payment_intent(self, payment_intent_id):
        """
        Confirm a PaymentIntent.
        
        CRITICAL: Uses stripe_account header.
        """
        return stripe.PaymentIntent.confirm(
            payment_intent_id,
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )
    
    def refund_payment(self, payment_intent_id, amount=None, reason='requested_by_customer'):
        """
        Refund a payment (full or partial).
        
        CRITICAL: Uses stripe_account header.
        
        Args:
            payment_intent_id: Stripe PaymentIntent ID
            amount: Optional partial refund amount in cents
            reason: Refund reason
        
        Returns:
            Refund object
        """
        refund_params = {
            'payment_intent': payment_intent_id,
            'reason': reason,
            'stripe_account': self.tenant.stripe_connect_id,  # CRITICAL
        }
        
        if amount:
            refund_params['amount'] = int(amount * 100)  # Convert to cents
        
        return stripe.Refund.create(**refund_params)
    
    def get_terminal_token(self):
        """
        Get a Stripe Terminal connection token for in-person payments.
        
        CRITICAL: Uses stripe_account header for tenant's terminal.
        
        Returns:
            dict: Connection token with secret
        """
        return stripe.terminal.ConnectionToken.create(
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )
    
    def get_payment_intent(self, payment_intent_id):
        """
        Retrieve a PaymentIntent.
        
        CRITICAL: Uses stripe_account header.
        """
        return stripe.PaymentIntent.retrieve(
            payment_intent_id,
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )
    
    def list_payment_methods(self, customer_id, type='card'):
        """
        List payment methods for a customer.

        CRITICAL: Uses stripe_account header.
        """
        return stripe.PaymentMethod.list(
            customer=customer_id,
            type=type,
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )

    def create_or_get_customer(self, user):
        """
        Create or retrieve a Stripe Customer for a user.

        CRITICAL: Uses stripe_account header to create customer on connected account.

        Args:
            user: User instance

        Returns:
            str: Stripe Customer ID
        """
        # Check if user already has a customer ID for this tenant
        stripe_customer_id = getattr(user, 'stripe_customer_id', None)

        if stripe_customer_id:
            # Verify customer exists on connected account
            try:
                stripe.Customer.retrieve(
                    stripe_customer_id,
                    stripe_account=self.tenant.stripe_connect_id
                )
                return stripe_customer_id
            except stripe.error.InvalidRequestError:
                # Customer doesn't exist on this account, create new one
                pass

        # Create new customer on connected account
        customer = stripe.Customer.create(
            email=user.email,
            name=user.full_name or user.username,
            metadata={
                'user_id': str(user.id),
                'tenant_id': str(self.tenant.id),
            },
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )

        # Save customer ID to user
        user.stripe_customer_id = customer.id
        user.save(update_fields=['stripe_customer_id'])

        return customer.id

    def create_setup_intent(self, customer_id):
        """
        Create a SetupIntent for saving a payment method without charging.

        CRITICAL: Uses stripe_account header.

        Args:
            customer_id: Stripe Customer ID

        Returns:
            SetupIntent object with client_secret
        """
        return stripe.SetupIntent.create(
            customer=customer_id,
            payment_method_types=['card'],
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )

    def detach_payment_method(self, payment_method_id):
        """
        Detach (remove) a payment method from a customer.

        CRITICAL: Uses stripe_account header.

        Args:
            payment_method_id: Stripe PaymentMethod ID

        Returns:
            PaymentMethod object
        """
        return stripe.PaymentMethod.detach(
            payment_method_id,
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )

    def set_default_payment_method(self, customer_id, payment_method_id):
        """
        Set a payment method as the default for a customer.

        CRITICAL: Uses stripe_account header.

        Args:
            customer_id: Stripe Customer ID
            payment_method_id: Stripe PaymentMethod ID

        Returns:
            Customer object
        """
        return stripe.Customer.modify(
            customer_id,
            invoice_settings={'default_payment_method': payment_method_id},
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )

    def get_customer(self, customer_id):
        """
        Retrieve a customer to get their default payment method.

        CRITICAL: Uses stripe_account header.
        """
        return stripe.Customer.retrieve(
            customer_id,
            stripe_account=self.tenant.stripe_connect_id  # CRITICAL
        )


# Helper function for easy service instantiation
def get_stripe_service_for_tenant(tenant):
    """
    Factory function to get StripeService for a tenant.
    
    Usage:
        stripe_svc = get_stripe_service_for_tenant(request.tenant)
        pi, tx = stripe_svc.create_payment_intent(event, Decimal('100.00'))
    """
    if not hasattr(tenant, 'stripe_connect_id') or not tenant.stripe_connect_id:
        raise ValueError(f"Tenant {tenant.name} does not have a Stripe Connect account")
    
    return StripeService(tenant)
