"""
Atomic Payment Service with proper 3DS/3DS2 support
Implements SetupIntent-first architecture for secure payment flows
"""

import structlog
from typing import Dict, Any, Optional
from datetime import datetime
from shared.clients.stripe_client import stripe_client
from shared.exceptions.payment_exceptions import (
    PaymentVerificationError,
    SubscriptionCreationFailed,
    SetupIntentError,
    PaymentServiceError,
    SubscriptionUpdateFailed,
    PaymentMethodError,
    CustomerUpdateFailed
)
from shared.utils.retry import retry_with_backoff

# Configure logging
logger = structlog.get_logger()


class PaymentService:
    """
    Atomic Payment Service with proper 3DS/3DS2 support
    """
    
    def __init__(self):
        """Initialize payment service"""
        self.stripe_client = stripe_client
    
    async def create_customer(
        self,
        email: str,
        name: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Atomic: Create Stripe customer
        
        Args:
            email: Customer email
            name: Customer name
            metadata: Additional metadata
            
        Returns:
            Customer creation result
            
        Raises:
            PaymentServiceError: If customer creation fails
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.create_customer(email, name, metadata),
                max_retries=3,
                exceptions=(Exception,)
            )
            
            logger.info("Customer created successfully",
                       customer_id=result.id,
                       email=email)
            
            return result
            
        except Exception as e:
            logger.error(f"Failed to create customer: {str(e)}, email: {email}",
                        exc_info=True)
            raise PaymentServiceError(f"Customer creation failed: {str(e)}") from e
    
    async def attach_payment_method(
        self,
        payment_method_id: str,
        customer_id: str
    ) -> Dict[str, Any]:
        """
        Atomic: Attach payment method to customer
        
        Args:
            payment_method_id: Payment method ID
            customer_id: Customer ID
            
        Returns:
            Payment method attachment result
            
        Raises:
            PaymentServiceError: If attachment fails
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.attach_payment_method(payment_method_id, customer_id),
                max_retries=3,
                exceptions=(Exception,)
            )
            
            logger.info("Payment method attached successfully",
                       payment_method_id=payment_method_id,
                       customer_id=customer_id)
            
            return result
            
        except Exception as e:
            logger.error(f"Failed to attach payment method: {str(e)}, payment_method_id: {payment_method_id}, customer_id: {customer_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Payment method attachment failed: {str(e)}") from e
    
    async def set_default_payment_method(
        self,
        customer_id: str,
        payment_method_id: str
    ) -> Dict[str, Any]:
        """
        Atomic: Set default payment method for customer
        
        Args:
            customer_id: Customer ID
            payment_method_id: Payment method ID
            
        Returns:
            Default payment method update result
            
        Raises:
            PaymentServiceError: If update fails
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.set_default_payment_method(customer_id, payment_method_id),
                max_retries=3,
                exceptions=(Exception,)
            )
            
            logger.info("Default payment method set successfully",
                       customer_id=customer_id,
                       payment_method_id=payment_method_id)
            
            return result
            
        except Exception as e:
            logger.error(f"Failed to set default payment method: {str(e)}, customer_id: {customer_id}, payment_method_id: {payment_method_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Default payment method update failed: {str(e)}") from e

    async def create_setup_intent_for_verification(
        self,
        customer_id: str,
        payment_method_id: str,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Create SetupIntent for payment verification.

        Args:
            customer_id: Stripe customer ID
            payment_method_id: Payment method ID to verify
            metadata: Additional metadata for tracking

        Returns:
            SetupIntent result for frontend confirmation

        Raises:
            SetupIntentError: If SetupIntent creation fails
        """
        try:
            full_metadata = metadata or {}
            full_metadata.update({
                'service': 'tenant',
                'operation': 'verification_setup_intent',
                'timestamp': datetime.now().isoformat()
            })

            result = await retry_with_backoff(
                lambda: self.stripe_client.create_setup_intent_for_verification(
                    customer_id, payment_method_id, full_metadata
                ),
                max_retries=3,
                exceptions=(SetupIntentError,)
            )

            logger.info("SetupIntent created for verification",
                       setup_intent_id=result['setup_intent_id'],
                       customer_id=customer_id,
                       requires_action=result['requires_action'],
                       status=result['status'])

            return result

        except SetupIntentError as e:
            logger.error(f"SetupIntent creation for verification failed: {str(e)}",
                        extra={"customer_id": customer_id, "payment_method_id": payment_method_id},
                        exc_info=True)
            raise
        except Exception as e:
            logger.error(f"Unexpected error creating SetupIntent for verification: {str(e)}",
                        extra={"customer_id": customer_id, "payment_method_id": payment_method_id},
                        exc_info=True)
            raise SetupIntentError(f"Unexpected SetupIntent error: {str(e)}") from e

    # Alias for backward compatibility
    async def create_setup_intent_for_registration(
        self,
        customer_id: str,
        payment_method_id: str,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Create standalone SetupIntent for payment verification during registration.
        This is an alias for create_setup_intent_for_verification for backward compatibility.

        Args:
            customer_id: Stripe customer ID
            payment_method_id: Payment method ID to verify
            metadata: Additional metadata for tracking

        Returns:
            SetupIntent result for frontend confirmation

        Raises:
            SetupIntentError: If SetupIntent creation fails
        """
        return await self.create_setup_intent_for_verification(customer_id, payment_method_id, metadata)

    async def create_setup_intent(
        self
    ) -> Dict[str, Any]:
        """
        Create a basic SetupIntent.

        Returns:
            SetupIntent creation result
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.create_setup_intent(),
                max_retries=3,
                exceptions=(SetupIntentError,)
            )

            logger.info("Basic SetupIntent created",
                       setup_intent_id=result['setup_intent_id'],
                       status=result['status'])

            return result

        except SetupIntentError as e:
            logger.error(f"Basic SetupIntent creation failed: {str(e)}",
                        exc_info=True)
            raise
        except Exception as e:
            logger.error(f"Unexpected error creating basic SetupIntent: {str(e)}",
                        exc_info=True)
            raise SetupIntentError(f"Unexpected SetupIntent error: {str(e)}") from e

    async def get_setup_intent(
        self,
        setup_intent_id: str
    ) -> Any:
        """
        Get SetupIntent details.

        Args:
            setup_intent_id: SetupIntent ID

        Returns:
            SetupIntent object
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.get_setup_intent(setup_intent_id),
                max_retries=3,
                exceptions=(SetupIntentError,)
            )

            logger.info("SetupIntent retrieved",
                       setup_intent_id=setup_intent_id)

            return result

        except SetupIntentError as e:
            logger.error(f"SetupIntent retrieval failed: {str(e)}",
                        extra={"setup_intent_id": setup_intent_id},
                        exc_info=True)
            raise
        except Exception as e:
            logger.error(f"Unexpected error retrieving SetupIntent: {str(e)}",
                        extra={"setup_intent_id": setup_intent_id},
                        exc_info=True)
            raise SetupIntentError(f"Unexpected SetupIntent error: {str(e)}") from e

    async def create_payment_intent(
        self,
        amount: float,
        currency: str,
        customer_id: str,
        payment_method_id: str
    ) -> Dict[str, Any]:
        """
        Create a PaymentIntent for one-time payments.

        Args:
            amount: Payment amount
            currency: Currency code
            customer_id: Customer ID
            payment_method_id: Payment method ID

        Returns:
            PaymentIntent creation result
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.create_payment_intent(
                    amount, currency, customer_id, payment_method_id
                ),
                max_retries=3,
                exceptions=(PaymentVerificationError,)
            )

            logger.info("PaymentIntent created",
                       payment_intent_id=result['payment_intent_id'],
                       status=result['status'])

            return result

        except PaymentVerificationError as e:
            logger.error(f"PaymentIntent creation failed: {str(e)}",
                        extra={
                            "amount": amount,
                            "currency": currency,
                            "customer_id": customer_id,
                            "payment_method_id": payment_method_id
                        },
                        exc_info=True)
            raise
        except Exception as e:
            logger.error(f"Unexpected error creating PaymentIntent: {str(e)}",
                        extra={
                            "amount": amount,
                            "currency": currency,
                            "customer_id": customer_id,
                            "payment_method_id": payment_method_id
                        },
                        exc_info=True)
            raise PaymentVerificationError(f"Unexpected payment error: {str(e)}") from e

    async def create_setup_intent_for_verification(
        self,
        customer_id: str,
        payment_method_id: str,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Create SetupIntent for registration payment verification.

        NEW ARCHITECTURE: Only creates SetupIntent, no subscription.
        Subscription is created after verification completes.

        Args:
            customer_id: Stripe customer ID
            payment_method_id: Payment method ID to verify
            metadata: Additional metadata for tracking

        Returns:
            SetupIntent result for frontend confirmation

        Raises:
            SetupIntentError: If SetupIntent creation fails
        """
        try:
            full_metadata = metadata or {}
            full_metadata.update({
                'service': 'tenant',
                'operation': 'registration_setup_intent',
                'timestamp': datetime.now().isoformat()
            })

            result = await retry_with_backoff(
                lambda: self.stripe_client.create_setup_intent_for_registration(
                    customer_id, payment_method_id, full_metadata
                ),
                max_retries=3,
                exceptions=(SetupIntentError,)
            )

            logger.info("SetupIntent created for registration",
                       setup_intent_id=result['setup_intent_id'],
                       customer_id=customer_id,
                       requires_action=result['requires_action'],
                       status=result['status'])

            return result

        except SetupIntentError as e:
            logger.error(f"SetupIntent creation failed: {str(e)}",
                        extra={"customer_id": customer_id, "payment_method_id": payment_method_id},
                        exc_info=True)
            raise
        except Exception as e:
            logger.error(f"Unexpected error creating SetupIntent: {str(e)}",
                        extra={"customer_id": customer_id, "payment_method_id": payment_method_id},
                        exc_info=True)
            raise SetupIntentError(f"Unexpected SetupIntent error: {str(e)}") from e

    async def create_subscription_after_verification(
        self,
        customer_id: str,
        price_id: str,
        payment_method_id: str,
        trial_period_days: Optional[int] = None,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Create subscription AFTER SetupIntent verification succeeds.

        NEW ARCHITECTURE: Called only after payment verification completes.

        Args:
            customer_id: Stripe customer ID
            price_id: Stripe price ID for the plan
            payment_method_id: Verified payment method ID
            trial_period_days: Optional trial period in days
            metadata: Additional metadata

        Returns:
            Subscription creation result

        Raises:
            SubscriptionCreationFailed: If subscription creation fails
        """
        try:
            full_metadata = metadata or {}
            full_metadata.update({
                'service': 'tenant',
                'operation': 'registration_subscription',
                'timestamp': datetime.now().isoformat()
            })

            result = await retry_with_backoff(
                lambda: self.stripe_client.create_subscription_after_verification(
                    customer_id, price_id, payment_method_id, trial_period_days, full_metadata
                ),
                max_retries=3,
                exceptions=(SubscriptionCreationFailed,)
            )

            logger.info("Subscription created after verification",
                       subscription_id=result['subscription_id'],
                       customer_id=customer_id,
                       status=result['status'],
                       trial_period_days=trial_period_days)

            return result

        except SubscriptionCreationFailed as e:
            logger.error(f"Subscription creation failed: {str(e)}",
                        extra={"customer_id": customer_id, "price_id": price_id},
                        exc_info=True)
            raise
        except Exception as e:
            logger.error(f"Unexpected error creating subscription: {str(e)}",
                        extra={"customer_id": customer_id, "price_id": price_id},
                        exc_info=True)
            raise SubscriptionCreationFailed(f"Unexpected subscription error: {str(e)}") from e
    
    async def verify_setup_intent(
        self,
        setup_intent_id: str
    ) -> Dict[str, Any]:
        """
        Verify SetupIntent status after frontend confirmation.

        Args:
            setup_intent_id: SetupIntent ID to verify

        Returns:
            SetupIntent verification result with 'verified' boolean

        Raises:
            SetupIntentError: If retrieval fails
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.verify_setup_intent_status(setup_intent_id),
                max_retries=3,
                exceptions=(SetupIntentError,)
            )

            logger.info("SetupIntent verification completed",
                       setup_intent_id=setup_intent_id,
                       status=result['status'],
                       verified=result.get('verified', False))

            return result

        except SetupIntentError as e:
            logger.error(f"SetupIntent verification failed: {str(e)}",
                        extra={"setup_intent_id": setup_intent_id},
                        exc_info=True)
            raise
        except Exception as e:
            logger.error(f"Unexpected error verifying SetupIntent: {str(e)}",
                        extra={"setup_intent_id": setup_intent_id},
                        exc_info=True)
            raise SetupIntentError(f"Unexpected verification error: {str(e)}") from e
    
    async def create_subscription_with_verified_payment(
        self,
        customer_id: str,
        price_id: str,
        payment_method_id: str,
        trial_period_days: Optional[int] = None,
        billing_cycle_anchor: Optional[int] = None,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Atomic: Create subscription with verified payment method
        ONLY called after successful SetupIntent verification
        
        Args:
            customer_id: Stripe customer ID
            price_id: Stripe price ID
            payment_method_id: Verified payment method ID
            trial_period_days: Optional trial period
            billing_cycle_anchor: Optional billing cycle anchor
            metadata: Additional metadata
            
        Returns:
            Subscription creation result
            
        Raises:
            SubscriptionCreationFailed: If subscription creation fails
        """
        try:
            # Add registration-specific metadata
            full_metadata = metadata or {}
            full_metadata.update({
                'service': 'tenant',
                'operation': 'registration_subscription_creation',
                'timestamp': datetime.now().isoformat()
            })
            
            result = await retry_with_backoff(
                lambda: self.stripe_client.create_subscription_with_verified_payment(
                    customer_id, price_id, payment_method_id, trial_period_days,
                    billing_cycle_anchor, full_metadata
                ),
                max_retries=3,
                exceptions=(SubscriptionCreationFailed,)
            )
            
            logger.info("Subscription created successfully",
                       subscription_id=result['subscription_id'],
                       customer_id=customer_id,
                       status=result['status'],
                       trial_period_days=trial_period_days)
            
            # CRITICAL FIX: Ensure that subscriptions with trial periods don't have immediate payment attempts
            # IMPORTANT: This validation distinguishes between 3DS verification (GOOD) and immediate payment (BAD)
            if trial_period_days and trial_period_days > 0:
                # For trial subscriptions, we need to ensure no immediate payment is attempted
                # This is a safeguard against Stripe creating invoices during trial periods
                # However, we MUST allow requires_action=True when it's for 3DS verification (SetupIntent)
                if result.get('requires_action', False):
                    # Check if this is for 3DS verification (setup_intent) vs immediate payment (payment_intent)
                    # - setup_intent_id present = 3DS verification needed = ALLOW (frontend handles verification)
                    # - payment_intent_id present = immediate payment needed = REJECT (shouldn't happen for trials)
                    # - no intent IDs = unexpected error = REJECT
                    setup_intent_id = result.get('setup_intent_id')
                    payment_intent_id = result.get('payment_intent_id')
                    
                    if setup_intent_id:
                        # This is 3DS verification - this is EXPECTED and GOOD
                        logger.info("Trial subscription requires 3DS verification via SetupIntent",
                                 subscription_id=result['subscription_id'],
                                 trial_period_days=trial_period_days,
                                 setup_intent_id=setup_intent_id)
                    elif payment_intent_id:
                        # This is immediate payment - this is BAD for trials
                        logger.warning("Subscription with trial period requires immediate payment action - this should not happen!",
                                     subscription_id=result['subscription_id'],
                                     trial_period_days=trial_period_days,
                                     payment_intent_id=payment_intent_id)
                        
                        # This is a critical error - trial periods should not require immediate payment
                        raise SubscriptionCreationFailed(
                            f"Subscription with {trial_period_days} day trial requires immediate payment. "
                            f"This indicates a Stripe configuration issue. "
                            f"Subscription ID: {result['subscription_id']}"
                        )
                    else:
                        # requires_action=True but no setup_intent_id or payment_intent_id - this is unexpected
                        logger.warning("Subscription with trial period requires action but has no setup_intent_id or payment_intent_id",
                                     subscription_id=result['subscription_id'],
                                     trial_period_days=trial_period_days)
                        raise SubscriptionCreationFailed(
                            f"Subscription with {trial_period_days} day trial requires action but has no associated intent. "
                            f"Subscription ID: {result['subscription_id']}"
                        )
                
                # Ensure subscription status is correct for trial periods
                if result.get('status') not in ['trialing', 'active']:
                    logger.error("Invalid subscription status for trial period",
                               subscription_id=result['subscription_id'],
                               status=result.get('status'),
                               trial_period_days=trial_period_days)
                    raise SubscriptionCreationFailed(
                        f"Invalid subscription status '{result.get('status')}' for trial period. "
                        f"Expected 'trialing' or 'active'. Subscription ID: {result['subscription_id']}"
                    )
            
            return result
            
        except SubscriptionCreationFailed as e:
            logger.error(f"Subscription creation failed: {str(e)}, customer_id: {customer_id}, price_id: {price_id}",
                        exc_info=True)
            raise
        except Exception as e:
            logger.error(f"Unexpected error creating subscription: {str(e)}, customer_id: {customer_id}, price_id: {price_id}",
                        exc_info=True)
            raise SubscriptionCreationFailed(f"Unexpected subscription error: {str(e)}") from e
    
    async def complete_registration_payment_flow(
        self,
        user_data: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Complete registration payment flow with atomic operations
        This orchestrates the entire payment verification and subscription creation
        
        Args:
            user_data: User registration data including payment info
            
        Returns:
            Complete registration payment result
            
        Raises:
            PaymentServiceError: If any step fails
        """
        try:
            # Extract required data
            email = user_data['email']
            full_name = user_data.get('full_name')
            payment_method_id = user_data['payment_method_id']
            plan_id = user_data['plan_id']
            billing_cycle = user_data.get('billing_cycle', 'monthly')
            coupon_code = user_data.get('coupon_code')
            
            # Step 1: Create customer
            logger.info("Step 1: Creating Stripe customer for registration",
                       email=email)
            
            customer_result = await self.create_customer(
                email=email,
                name=full_name,
                metadata={
                    'registration_flow': 'secure_registration',
                    'plan_id': plan_id
                }
            )
            customer_id = customer_result['customer_id']
            
            # Step 2: Attach payment method
            logger.info("Step 2: Attaching payment method to customer",
                       customer_id=customer_id,
                       payment_method_id=payment_method_id)
            
            await self.attach_payment_method(
                payment_method_id=payment_method_id,
                customer_id=customer_id
            )
            
            # Step 3: Set as default payment method
            logger.info("Step 3: Setting default payment method",
                       customer_id=customer_id,
                       payment_method_id=payment_method_id)
            
            await self.set_default_payment_method(
                customer_id=customer_id,
                payment_method_id=payment_method_id
            )
            
            # Step 4: Create SetupIntent for verification
            logger.info("Step 4: Creating SetupIntent for payment verification",
                       customer_id=customer_id,
                       payment_method_id=payment_method_id)
            
            setup_intent_result = await self.create_setup_intent_for_verification(
                customer_id=customer_id,
                payment_method_id=payment_method_id,
                metadata={
                    'email': email,
                    'plan_id': plan_id,
                    'billing_cycle': billing_cycle
                }
            )
            
            # Return result with SetupIntent for frontend to handle
            # Frontend will confirm SetupIntent and handle 3DS if required
            return {
                'success': True,
                'customer_id': customer_id,
                'payment_customer_id': customer_id,
                'setup_intent_id': setup_intent_result['setup_intent_id'],
                'client_secret': setup_intent_result['client_secret'],
                'requires_action': setup_intent_result['requires_action'],
                'status': setup_intent_result['status'],
                'email': email,
                'plan_id': plan_id,
                'payment_method_id': payment_method_id,
                'billing_cycle': billing_cycle,
                'message': 'Payment verification required. Frontend must confirm SetupIntent.'
            }
            
        except Exception as e:
            logger.error(f"Registration payment flow failed: {str(e)}, email: {user_data.get('email')}",
                        exc_info=True)
            raise PaymentServiceError(f"Registration payment flow failed: {str(e)}") from e
    
    async def complete_subscription_after_verification(
        self,
        setup_intent_id: str,
        user_data: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Complete subscription creation after successful SetupIntent verification
        
        Args:
            setup_intent_id: Verified SetupIntent ID
            user_data: User registration data
            
        Returns:
            Subscription creation result
            
        Raises:
            PaymentServiceError: If subscription creation fails
        """
        try:
            # Step 1: Verify SetupIntent
            logger.info("Verifying SetupIntent before subscription creation",
                       setup_intent_id=setup_intent_id)
            
            verification_result = await self.verify_setup_intent_status(setup_intent_id)
            
            if not verification_result.get('verified', False):
                raise PaymentServiceError("SetupIntent verification failed before subscription creation")
            
            customer_id = verification_result['customer_id']
            payment_method_id = verification_result['payment_method_id']
            
            # Step 2: Get plan details and price ID
            plan_id = user_data['plan_id']
            billing_cycle = user_data.get('billing_cycle', 'monthly')
            
            logger.info(f"Mapping plan to Stripe price ID", 
                       plan_id=plan_id, 
                       billing_cycle=billing_cycle)
            
            # Map plan to Stripe price ID (this would come from config in real implementation)
            price_id = self._get_stripe_price_id(plan_id, billing_cycle)
            
            logger.info(f"Retrieved Stripe price ID", 
                       plan_id=plan_id, 
                       billing_cycle=billing_cycle,
                       price_id=price_id)
            
            # Step 3: Create subscription with verified payment method
            logger.info("Creating subscription with verified payment method",
                       customer_id=customer_id,
                       plan_id=plan_id,
                       payment_method_id=payment_method_id)
            
            subscription_result = await self.create_subscription_with_verified_payment(
                customer_id=customer_id,
                price_id=price_id,
                payment_method_id=payment_method_id,
                trial_period_days=user_data.get('trial_period_days'),
                metadata={
                    'registration_flow': 'secure_registration',
                    'email': user_data.get('email'),
                    'plan_id': plan_id
                }
            )
            
            return {
                'success': True,
                'subscription_id': subscription_result['subscription_id'],
                'customer_id': customer_id,
                'payment_customer_id': customer_id,
                'status': subscription_result['status'],
                'plan_id': plan_id,
                'payment_method_id': payment_method_id,
                'trial_period_days': subscription_result.get('trial_period_days'),
                'current_period_end': subscription_result['current_period_end'],
                'message': 'Subscription created successfully with verified payment method'
            }
            
        except Exception as e:
            logger.error(f"Subscription creation after verification failed: {str(e)}, setup_intent_id: {setup_intent_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Subscription creation failed: {str(e)}") from e
    
    def _get_stripe_price_id(self, plan_id: str, billing_cycle: str) -> str:
        """
        Get Stripe price ID for a given plan and billing cycle
        Uses configuration from tenant service settings
        """
        try:
            from app.core.config import settings
            
            # Use the configured price ID mapping
            price_id = settings.STRIPE_PRICE_ID_MAPPING.get((plan_id.lower(), billing_cycle.lower()))
            
            logger.info(f"Getting Stripe price ID for plan {plan_id} ({billing_cycle})", 
                       price_id=price_id, 
                       plan_id=plan_id, 
                       billing_cycle=billing_cycle)
            
            if not price_id:
                raise PaymentServiceError(f"No Stripe price ID found for plan {plan_id} with {billing_cycle} billing")
            
            return price_id
        except ImportError:
            # Fallback to hardcoded values if config import fails
            # This should only happen in testing environments
            # Use actual Stripe price IDs from config
            logger.warning("Using fallback price ID mapping - config import failed")
            
            price_mapping = {
                ('starter', 'monthly'): 'price_1Sp0p3IzCdnBmAVT2Gs7z5np',
                ('starter', 'yearly'): 'price_1Sp0twIzCdnBmAVTD1lNLedx',
                ('professional', 'monthly'): 'price_1Sp0w7IzCdnBmAVTp0Jxhh1u',
                ('professional', 'yearly'): 'price_1Sp0yAIzCdnBmAVTLoGl4QCb',
                ('enterprise', 'monthly'): 'price_1Sp0zAIzCdnBmAVTXpApF7YO',
                ('enterprise', 'yearly'): 'price_1Sp15mIzCdnBmAVTuxffMpV5',
            }
            
            price_id = price_mapping.get((plan_id.lower(), billing_cycle.lower()))
            
            logger.info(f"Fallback: Getting Stripe price ID for plan {plan_id} ({billing_cycle})", 
                       price_id=price_id, 
                       plan_id=plan_id, 
                       billing_cycle=billing_cycle)
            
            if not price_id:
                raise PaymentServiceError(f"No Stripe price ID found for plan {plan_id} with {billing_cycle} billing")

            return price_id

    async def update_payment_subscription(
        self,
        subscription_id: str,
        new_price_id: str,
        proration_behavior: str = "create_prorations",
        billing_cycle_anchor: Optional[str] = None,
        payment_behavior: str = "error_if_incomplete",
        immediate_change: bool = True,
        preserve_trial: bool = False
    ) -> Any:
        """
        Update subscription price (plan upgrade/downgrade)

        Args:
            subscription_id: Stripe subscription ID
            new_price_id: New Stripe price ID
            proration_behavior: How to handle proration ('create_prorations', 'none', 'always_invoice')
            billing_cycle_anchor: Billing cycle anchor ("now" or "unchanged")
            payment_behavior: Payment behavior on update
            immediate_change: Whether to apply changes immediately
            preserve_trial: If True, preserves the trial period after upgrade

        Returns:
            Updated subscription object with .id, .status, etc. attributes
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.update_subscription(
                    subscription_id,
                    new_price_id,
                    proration_behavior=proration_behavior,
                    preserve_trial=preserve_trial
                ),
                max_retries=3,
                exceptions=(SubscriptionCreationFailed,)
            )

            logger.info("Subscription updated successfully",
                       subscription_id=subscription_id,
                       new_price_id=new_price_id,
                       proration_behavior=proration_behavior,
                       preserve_trial=preserve_trial,
                       is_trialing=result.get('is_trialing', False))

            # Create wrapper object for compatibility with callers expecting .id, .status etc.
            class SubscriptionWrapper:
                def __init__(self, data: dict):
                    self.id = data.get('subscription_id')
                    self.status = data.get('status')
                    self.current_period_start = data.get('current_period_start')
                    self.current_period_end = data.get('current_period_end')
                    self.customer = data.get('customer_id')
                    self.trial_end = data.get('trial_end')
                    self.is_trialing = data.get('is_trialing', False)

            return SubscriptionWrapper(result)

        except Exception as e:
            logger.error(f"Failed to update subscription: {str(e)}, subscription_id: {subscription_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Subscription update failed: {str(e)}") from e

    async def cancel_payment_subscription(
        self,
        subscription_id: str
    ) -> Any:
        """
        Cancel a subscription at period end

        Args:
            subscription_id: Stripe subscription ID

        Returns:
            Cancelled subscription object
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.cancel_subscription(subscription_id),
                max_retries=3,
                exceptions=(SubscriptionCreationFailed,)
            )

            logger.info("Subscription cancelled successfully",
                       subscription_id=subscription_id)

            return result

        except Exception as e:
            logger.error(f"Failed to cancel subscription: {str(e)}, subscription_id: {subscription_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Subscription cancellation failed: {str(e)}") from e

    async def calculate_payment_proration(
        self,
        subscription_id: str,
        new_price_id: str,
        proration_behavior: str = "create_prorations"
    ) -> Dict[str, Any]:
        """
        Calculate proration for subscription plan change

        Args:
            subscription_id: Current subscription ID
            new_price_id: New price ID to change to
            proration_behavior: Proration behavior

        Returns:
            Proration calculation details
        """
        try:
            # Get current subscription to calculate proration
            subscription = await self.stripe_client.get_subscription(subscription_id)

            # For now, return a simple proration estimate
            # In production, use Stripe's upcoming invoice preview
            return {
                'subscription_id': subscription_id,
                'new_price_id': new_price_id,
                'proration_behavior': proration_behavior,
                'net_amount': 0,  # Placeholder - should use Stripe's invoice preview
                'calculated_at': datetime.now().isoformat()
            }

        except Exception as e:
            logger.error(f"Failed to calculate proration: {str(e)}, subscription_id: {subscription_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Proration calculation failed: {str(e)}") from e

    async def change_billing_cycle(
        self,
        subscription_id: str,
        new_billing_cycle: str,
        proration_behavior: str = "create_prorations"
    ) -> Any:
        """
        Change subscription billing cycle

        Args:
            subscription_id: Subscription ID
            new_billing_cycle: New billing cycle (monthly/yearly)
            proration_behavior: How to handle proration

        Returns:
            Updated subscription object with .id, .status, etc. attributes
        """
        try:
            # Get current subscription
            subscription = await self.stripe_client.get_subscription(subscription_id)

            # This would need to get the correct price ID for the new billing cycle
            logger.info("Billing cycle change requested",
                       subscription_id=subscription_id,
                       new_billing_cycle=new_billing_cycle,
                       proration_behavior=proration_behavior)

            # Create wrapper object for compatibility
            class SubscriptionWrapper:
                def __init__(self, data: dict):
                    self.id = data.get('subscription_id')
                    self.status = data.get('status')
                    self.current_period_start = data.get('current_period_start')
                    self.current_period_end = data.get('current_period_end')
                    self.customer = data.get('customer_id')

            return SubscriptionWrapper(subscription)

        except Exception as e:
            logger.error(f"Failed to change billing cycle: {str(e)}, subscription_id: {subscription_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Billing cycle change failed: {str(e)}") from e

    async def get_customer_payment_method(
        self,
        customer_id: str
    ) -> Optional[Dict[str, Any]]:
        """
        Get customer's default payment method

        Args:
            customer_id: Stripe customer ID

        Returns:
            Payment method details or None
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.get_customer_payment_method(customer_id),
                max_retries=3,
                exceptions=(Exception,)
            )

            return result

        except Exception as e:
            logger.error(f"Failed to get customer payment method: {str(e)}, customer_id: {customer_id}",
                        exc_info=True)
            return None

    async def update_payment_method(
        self,
        customer_id: str,
        payment_method_id: str
    ) -> Dict[str, Any]:
        """
        Update customer's payment method

        Args:
            customer_id: Stripe customer ID
            payment_method_id: New payment method ID

        Returns:
            Payment method update result
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.update_payment_method(customer_id, payment_method_id),
                max_retries=3,
                exceptions=(Exception,)
            )

            logger.info("Payment method updated successfully",
                       customer_id=customer_id,
                       payment_method_id=payment_method_id)

            return result

        except Exception as e:
            logger.error(f"Failed to update payment method: {str(e)}, customer_id: {customer_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Payment method update failed: {str(e)}") from e

    async def complete_subscription_after_setup_intent(
        self,
        setup_intent_id: str,
        customer_id: Optional[str] = None,
        plan_id: Optional[str] = None,
        payment_method_id: Optional[str] = None,
        trial_period_days: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        Complete subscription after SetupIntent verification

        Args:
            setup_intent_id: SetupIntent ID
            customer_id: Stripe customer ID (optional, retrieved from SetupIntent if not provided)
            plan_id: Plan ID for subscription creation
            payment_method_id: Payment method ID (optional, retrieved from SetupIntent if not provided)
            trial_period_days: Optional trial period

        Returns:
            Dictionary with 'subscription' key containing subscription details
        """
        try:
            # Step 1: Verify the SetupIntent
            verification_result = await retry_with_backoff(
                lambda: self.stripe_client.verify_setup_intent_status(setup_intent_id),
                max_retries=3,
                exceptions=(SetupIntentError,)
            )

            if not verification_result.get('verified'):
                raise PaymentServiceError(
                    f"SetupIntent verification failed: {verification_result.get('last_setup_error', 'Unknown error')}"
                )

            # Use values from SetupIntent if not provided
            actual_customer_id = customer_id or verification_result.get('customer_id')
            actual_payment_method_id = payment_method_id or verification_result.get('payment_method_id')

            logger.info("SetupIntent verified, creating subscription",
                       setup_intent_id=setup_intent_id,
                       customer_id=actual_customer_id)

            # Step 2: Create the subscription if plan_id is provided
            if plan_id:
                # Get the billing interval (default to monthly)
                billing_interval = 'monthly'
                price_id = self._get_stripe_price_id(plan_id, billing_interval)

                subscription_result = await self.create_subscription_with_verified_payment(
                    customer_id=actual_customer_id,
                    price_id=price_id,
                    payment_method_id=actual_payment_method_id,
                    trial_period_days=trial_period_days
                )

                # Create a mock subscription object-like dict for compatibility
                # The caller expects result['subscription'].id, result['subscription'].status, etc.
                class SubscriptionResult:
                    def __init__(self, data: Dict[str, Any]):
                        self.id = data.get('subscription_id')
                        self.status = data.get('status')
                        self.current_period_start = data.get('current_period_start')
                        self.current_period_end = data.get('current_period_end')
                        self.customer = data.get('customer_id')

                return {
                    'subscription': SubscriptionResult(subscription_result),
                    'verification': verification_result
                }
            else:
                # No plan_id provided, just return verification result
                return {
                    'verification': verification_result,
                    'setup_intent_id': setup_intent_id,
                    'customer_id': actual_customer_id,
                    'payment_method_id': actual_payment_method_id
                }

        except Exception as e:
            logger.error(f"Failed to complete subscription after SetupIntent: {str(e)}, setup_intent_id: {setup_intent_id}",
                        exc_info=True)
            raise PaymentServiceError(f"Subscription completion failed: {str(e)}") from e

    async def verify_setup_intent_status(
        self,
        setup_intent_id: str
    ) -> Dict[str, Any]:
        """
        Verify SetupIntent status

        Args:
            setup_intent_id: SetupIntent ID to verify

        Returns:
            SetupIntent verification result
        """
        try:
            result = await retry_with_backoff(
                lambda: self.stripe_client.verify_setup_intent_status(setup_intent_id),
                max_retries=3,
                exceptions=(SetupIntentError,)
            )

            logger.info("SetupIntent verified",
                       setup_intent_id=setup_intent_id,
                       status=result.get('status'))

            return result

        except Exception as e:
            logger.error(f"Failed to verify SetupIntent: {str(e)}, setup_intent_id: {setup_intent_id}",
                        exc_info=True)
            raise PaymentServiceError(f"SetupIntent verification failed: {str(e)}") from e

    async def update_subscription_payment_method(
        self,
        subscription_id: str,
        payment_method_id: str
    ) -> Any:
        """
        Update an existing subscription with a new payment method
        
        Args:
            subscription_id: Stripe subscription ID
            payment_method_id: New payment method ID
            
        Returns:
            Updated Stripe subscription object
        """
        try:
            logger.info("Updating subscription payment method",
                       subscription_id=subscription_id,
                       payment_method_id=payment_method_id)
            
            # Update the subscription's default payment method
            stripe_subscription = await retry_with_backoff(
                lambda: self.stripe_client.update_subscription_payment_method(
                    subscription_id,
                    payment_method_id
                ),
                max_retries=3,
                exceptions=(SubscriptionUpdateFailed,)
            )
            
            logger.info("Subscription payment method updated successfully",
                       subscription_id=subscription_id,
                       status=stripe_subscription.status)
            
            return stripe_subscription
            
        except Exception as e:
            logger.error(f"Failed to update subscription payment method: {str(e)}, subscription_id: {subscription_id}",
                        exc_info=True)
            raise SubscriptionUpdateFailed(f"Failed to update payment method: {str(e)}") from e

    async def attach_payment_method_to_customer(
        self,
        customer_id: str,
        payment_method_id: str
    ) -> Any:
        """
        Attach a payment method to a customer
        
        Args:
            customer_id: Stripe customer ID
            payment_method_id: Payment method ID
            
        Returns:
            Updated payment method object
        """
        try:
            logger.info("Attaching payment method to customer",
                       customer_id=customer_id,
                       payment_method_id=payment_method_id)
            
            payment_method = await retry_with_backoff(
                lambda: self.stripe_client.attach_payment_method_to_customer(
                    customer_id,
                    payment_method_id
                ),
                max_retries=3,
                exceptions=(PaymentMethodError,)
            )
            
            logger.info("Payment method attached to customer successfully",
                       customer_id=customer_id,
                       payment_method_id=payment_method.id)
            
            return payment_method
            
        except Exception as e:
            logger.error(f"Failed to attach payment method to customer: {str(e)}, customer_id: {customer_id}",
                        exc_info=True)
            raise PaymentMethodError(f"Failed to attach payment method: {str(e)}") from e

    async def set_customer_default_payment_method(
        self,
        customer_id: str,
        payment_method_id: str
    ) -> Any:
        """
        Set a payment method as the customer's default payment method
        
        Args:
            customer_id: Stripe customer ID
            payment_method_id: Payment method ID
            
        Returns:
            Updated customer object
        """
        try:
            logger.info("Setting default payment method for customer",
                       customer_id=customer_id,
                       payment_method_id=payment_method_id)
            
            customer = await retry_with_backoff(
                lambda: self.stripe_client.set_customer_default_payment_method(
                    customer_id,
                    payment_method_id
                ),
                max_retries=3,
                exceptions=(CustomerUpdateFailed,)
            )
            
            logger.info("Default payment method set for customer successfully",
                       customer_id=customer.id,
                       payment_method_id=payment_method_id)
            
            return customer
            
        except Exception as e:
            logger.error(f"Failed to set default payment method for customer: {str(e)}, customer_id: {customer_id}",
                        exc_info=True)
            raise CustomerUpdateFailed(f"Failed to set default payment method: {str(e)}") from e


# Singleton instance for dependency injection
payment_service = PaymentService()