"""
Subscription Orchestration Service - Coordinator
High-level business workflow coordination for subscription operations
This service orchestrates complex workflows involving multiple services
"""

import structlog
from typing import Dict, Any, Optional, List
from datetime import datetime, timezone
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Session

from app.services.subscription_service import SubscriptionService
from app.services.payment_service import PaymentService
from app.services.coupon_service import CouponService
from app.services.tenant_service import EnhancedTenantService
from app.models.tenants import Subscription
from app.core.config import settings
from shared.database.exceptions import DatabaseError, ValidationError
from shared.database.base import create_database_manager
from shared.exceptions.payment_exceptions import SubscriptionUpdateFailed
from shared.exceptions.subscription_exceptions import SubscriptionNotFound

logger = structlog.get_logger()


class SubscriptionOrchestrationService:
    """Service for orchestrating complex subscription workflows"""

    def __init__(self, db_session: AsyncSession):
        self.db_session = db_session
        self.subscription_service = SubscriptionService(db_session)
        self.payment_service = PaymentService()

        # Create a synchronous session for coupon operations
        # Note: CouponService requires sync Session, not AsyncSession
        # This is a limitation that should be addressed in future refactoring
        self.coupon_service = None  # Will be initialized when needed with sync session

        # Initialize tenant service
        database_manager = create_database_manager(settings.DATABASE_URL, "tenant-service")
        self.tenant_service = EnhancedTenantService(database_manager)

    async def orchestrate_subscription_creation(
        self,
        tenant_id: str,
        user_data: Dict[str, Any],
        plan_id: str,
        payment_method_id: str,
        billing_interval: str = "monthly",
        coupon_code: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Orchestrate the complete subscription creation workflow

        Args:
            tenant_id: Tenant ID
            user_data: User data for customer creation
            plan_id: Subscription plan ID
            payment_method_id: Payment method ID from provider
            billing_interval: Billing interval (monthly/yearly)
            coupon_code: Optional coupon code

        Returns:
            Dictionary with subscription creation results
        """
        try:
            logger.info("Starting subscription creation orchestration",
                       tenant_id=tenant_id, plan_id=plan_id)

            # Step 1: Create customer in payment provider
            logger.info("Creating customer in payment provider",
                       tenant_id=tenant_id, email=user_data.get('email'))

            email = user_data.get('email')
            name = f"{user_data.get('first_name', '')} {user_data.get('last_name', '')}".strip()
            metadata = None
            customer = await self.payment_service.create_customer(email, name, metadata)
            logger.info("Customer created successfully",
                       customer_id=customer.id, tenant_id=tenant_id)

            # Step 2: Handle coupon logic (if provided)
            trial_period_days = 0
            coupon_discount = None

            if coupon_code:
                logger.info("Validating and redeeming coupon code",
                           coupon_code=coupon_code, tenant_id=tenant_id)

                coupon_service = CouponService(self.db_session)
                success, discount_applied, error = await coupon_service.redeem_coupon(
                    coupon_code,
                    tenant_id,
                    base_trial_days=0
                )

                if success and discount_applied:
                    coupon_discount = discount_applied
                    trial_period_days = discount_applied.get("total_trial_days", 0)
                    logger.info("Coupon redeemed successfully",
                               coupon_code=coupon_code,
                               trial_period_days=trial_period_days,
                               discount_applied=discount_applied)
                else:
                    logger.warning("Failed to redeem coupon, continuing without it",
                                 coupon_code=coupon_code,
                                 error=error)

            # Step 3: Create subscription in payment provider
            logger.info("Creating subscription in payment provider",
                       customer_id=customer.id,
                       plan_id=plan_id,
                       trial_period_days=trial_period_days)

            # Get the Stripe price ID for this plan
            price_id = self.payment_service._get_stripe_price_id(plan_id, billing_interval)
            
            stripe_subscription = await self.payment_service.create_subscription_with_verified_payment(
                customer.id,
                price_id,
                payment_method_id,
                trial_period_days if trial_period_days > 0 else None,
                billing_interval
            )

            logger.info("Subscription created in payment provider",
                       subscription_id=stripe_subscription.id,
                       status=stripe_subscription.status)

            # Step 4: Create local subscription record
            logger.info("Creating local subscription record",
                       tenant_id=tenant_id,
                       subscription_id=stripe_subscription.id)

            subscription_record = await self.subscription_service.create_subscription_record(
                tenant_id,
                stripe_subscription.id,
                customer.id,
                plan_id,
                stripe_subscription.status,
                trial_period_days if trial_period_days > 0 else None,
                billing_interval
            )

            logger.info("Local subscription record created",
                       subscription_id=stripe_subscription.id)

            # Step 5: Update tenant with subscription information
            logger.info("Updating tenant with subscription information",
                       tenant_id=tenant_id)

            tenant_update_data = {
                'customer_id': customer.id,
                'subscription_status': stripe_subscription.status,
                'subscription_plan': plan_id,
                'subscription_tier': plan_id,
                'billing_cycle': billing_interval,
                'trial_period_days': trial_period_days
            }

            await self.tenant_service.update_tenant_subscription_info(
                tenant_id, tenant_update_data
            )

            logger.info("Tenant updated with subscription information",
                       tenant_id=tenant_id)

            # Prepare final result
            # Convert current_period_end timestamp to ISO format if it's an integer
            current_period_end = stripe_subscription.current_period_end
            if isinstance(current_period_end, int):
                # Stripe returns Unix timestamp, convert to datetime then ISO format
                current_period_end = datetime.fromtimestamp(current_period_end, tz=timezone.utc).isoformat()
            elif hasattr(current_period_end, 'isoformat'):
                current_period_end = current_period_end.isoformat()
            else:
                current_period_end = str(current_period_end)

            result = {
                "success": True,
                "customer_id": customer.id,
                "subscription_id": stripe_subscription.id,
                "status": stripe_subscription.status,
                "plan": plan_id,
                "billing_cycle": billing_interval,
                "trial_period_days": trial_period_days,
                "current_period_end": current_period_end,
                "coupon_applied": bool(coupon_discount)
            }

            if coupon_discount:
                result["coupon_details"] = coupon_discount

            logger.info("Subscription creation orchestration completed successfully",
                       tenant_id=tenant_id,
                       subscription_id=stripe_subscription.id)

            return result

        except ValidationError as ve:
            logger.error("Subscription creation validation failed",
                       error=str(ve), tenant_id=tenant_id)
            raise ve

        except Exception as e:
            logger.error("Subscription creation orchestration failed",
                       error=str(e), tenant_id=tenant_id)
            raise DatabaseError(f"Failed to create subscription: {str(e)}")

    async def create_tenant_independent_subscription(
        self,
        user_data: Dict[str, Any],
        plan_id: str,
        payment_method_id: str,
        billing_interval: str = "monthly",
        coupon_code: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Create a subscription that is not linked to any tenant yet
        
        This subscription will be linked to a tenant during onboarding
        when the user creates their bakery/tenant.
        
        Args:
            user_data: User data for customer creation
            plan_id: Subscription plan ID
            payment_method_id: Payment method ID from provider
            billing_interval: Billing interval (monthly/yearly)
            coupon_code: Optional coupon code
            
        Returns:
            Dictionary with subscription creation results
        """
        try:
            logger.info("Starting tenant-independent subscription creation",
                       user_id=user_data.get('user_id'),
                       plan_id=plan_id)

            # Step 1: Create customer in payment provider
            logger.info("Creating customer in payment provider",
                       user_id=user_data.get('user_id'),
                       email=user_data.get('email'))

            email = user_data.get('email')
            name = f"{user_data.get('first_name', '')} {user_data.get('last_name', '')}".strip()
            metadata = None
            customer = await self.payment_service.create_customer(email, name, metadata)
            logger.info("Customer created successfully",
                       customer_id=customer.id,
                       user_id=user_data.get('user_id'))

            # Step 2: Handle coupon logic (if provided)
            trial_period_days = 0
            coupon_discount = None

            if coupon_code:
                logger.info("Validating and redeeming coupon code",
                           coupon_code=coupon_code,
                           user_id=user_data.get('user_id'))

                coupon_service = CouponService(self.db_session)
                success, discount_applied, error = await coupon_service.redeem_coupon(
                    coupon_code,
                    None,  # No tenant_id yet
                    base_trial_days=0
                )

                if success and discount_applied:
                    coupon_discount = discount_applied
                    trial_period_days = discount_applied.get("total_trial_days", 0)
                    logger.info("Coupon redeemed successfully",
                               coupon_code=coupon_code,
                               trial_period_days=trial_period_days,
                               discount_applied=discount_applied)
                else:
                    logger.warning("Failed to redeem coupon, continuing without it",
                                 coupon_code=coupon_code,
                                 error=error)

            # Step 3: Create subscription in payment provider (or get SetupIntent for 3DS)
            logger.info("Creating subscription in payment provider",
                       customer_id=customer.id,
                       plan_id=plan_id,
                       trial_period_days=trial_period_days)

            # Get the Stripe price ID for this plan
            price_id = self.payment_service._get_stripe_price_id(plan_id, billing_interval)
            
            subscription_result = await self.payment_service.create_subscription_with_verified_payment(
                customer.id,
                price_id,
                payment_method_id,
                trial_period_days if trial_period_days > 0 else None,
                billing_interval
            )

            # Check if result requires 3DS authentication (SetupIntent confirmation)
            if isinstance(subscription_result, dict) and subscription_result.get('requires_action'):
                logger.info("Subscription creation requires SetupIntent confirmation",
                           customer_id=customer.id,
                           action_type=subscription_result.get('action_type'),
                           setup_intent_id=subscription_result.get('setup_intent_id'))

                # Return the SetupIntent data for frontend to handle 3DS
                return {
                    "requires_action": True,
                    "action_type": subscription_result.get('action_type'),
                    "client_secret": subscription_result.get('client_secret'),
                    "setup_intent_id": subscription_result.get('setup_intent_id'),
                    "customer_id": customer.id,
                    "payment_method_id": payment_method_id,
                    "plan_id": plan_id,
                    "trial_period_days": trial_period_days,
                    "billing_interval": billing_interval,
                    "message": subscription_result.get('message'),
                    "user_id": user_data.get('user_id')
                }

            # Extract subscription object from result
            # Result can be either:
            # 1. A dict with 'subscription' key containing an object
            # 2. A dict with subscription fields directly (subscription_id, status, etc.)
            # 3. A subscription object directly
            if isinstance(subscription_result, dict):
                if 'subscription' in subscription_result:
                    stripe_subscription = subscription_result['subscription']
                elif 'subscription_id' in subscription_result:
                    # Create a simple object-like wrapper for dict results
                    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')
                    stripe_subscription = SubscriptionWrapper(subscription_result)
                else:
                    stripe_subscription = subscription_result
            else:
                stripe_subscription = subscription_result

            logger.info("Subscription created in payment provider",
                       subscription_id=stripe_subscription.id,
                       status=stripe_subscription.status)

            # Step 4: Create local subscription record WITHOUT tenant_id
            logger.info("Creating tenant-independent subscription record",
                       user_id=user_data.get('user_id'),
                       subscription_id=stripe_subscription.id)

            subscription_record = await self.subscription_service.create_tenant_independent_subscription_record(
                stripe_subscription.id,
                customer.id,
                plan_id,
                stripe_subscription.status,
                trial_period_days if trial_period_days > 0 else None,
                billing_interval,
                user_data.get('user_id')
            )

            logger.info("Tenant-independent subscription record created",
                       subscription_id=stripe_subscription.id,
                       user_id=user_data.get('user_id'))

            # Prepare final result
            # Convert current_period_end timestamp to ISO format if it's an integer
            current_period_end = stripe_subscription.current_period_end
            if isinstance(current_period_end, int):
                # Stripe returns Unix timestamp, convert to datetime then ISO format
                current_period_end = datetime.fromtimestamp(current_period_end, tz=timezone.utc).isoformat()
            elif hasattr(current_period_end, 'isoformat'):
                current_period_end = current_period_end.isoformat()
            else:
                current_period_end = str(current_period_end)

            result = {
                "success": True,
                "customer_id": customer.id,
                "subscription_id": stripe_subscription.id,
                "status": stripe_subscription.status,
                "plan": plan_id,
                "billing_cycle": billing_interval,
                "trial_period_days": trial_period_days,
                "current_period_end": current_period_end,
                "coupon_applied": bool(coupon_discount),
                "user_id": user_data.get('user_id')
            }

            if coupon_discount:
                result["coupon_details"] = coupon_discount

            logger.info("Tenant-independent subscription creation completed successfully",
                       user_id=user_data.get('user_id'),
                       subscription_id=stripe_subscription.id)

            return result

        except ValidationError as ve:
            logger.error("Tenant-independent subscription creation validation failed",
                       error=str(ve), user_id=user_data.get('user_id'))
            raise ve

        except Exception as e:
            logger.error("Tenant-independent subscription creation failed",
                       error=str(e), user_id=user_data.get('user_id'))
            raise DatabaseError(f"Failed to create tenant-independent subscription: {str(e)}")

    async def complete_subscription_after_setup_intent(
        self,
        setup_intent_id: str,
        customer_id: str,
        plan_id: str,
        payment_method_id: str,
        trial_period_days: Optional[int],
        user_id: str,
        billing_interval: str = "monthly"
    ) -> Dict[str, Any]:
        """
        Complete subscription creation after SetupIntent has been confirmed

        This method is called after the frontend successfully confirms a SetupIntent
        (with or without 3DS). It creates the subscription with the verified payment method
        and creates a database record.

        Args:
            setup_intent_id: The confirmed SetupIntent ID
            customer_id: Stripe customer ID
            plan_id: Subscription plan ID
            payment_method_id: Verified payment method ID
            trial_period_days: Optional trial period
            user_id: User ID for linking
            billing_interval: Billing interval

        Returns:
            Dictionary with subscription details
        """
        try:
            logger.info("Completing subscription after SetupIntent confirmation",
                       setup_intent_id=setup_intent_id,
                       user_id=user_id,
                       plan_id=plan_id)

            # Call payment service to complete subscription creation
            result = await self.payment_service.complete_subscription_after_setup_intent(
                setup_intent_id,
                customer_id,
                plan_id,
                payment_method_id,
                trial_period_days
            )

            stripe_subscription = result['subscription']

            logger.info("Subscription created in payment provider after SetupIntent",
                       subscription_id=stripe_subscription.id,
                       status=stripe_subscription.status)

            # Create local subscription record WITHOUT tenant_id (tenant-independent)
            subscription_record = await self.subscription_service.create_tenant_independent_subscription_record(
                stripe_subscription.id,
                customer_id,
                plan_id,
                stripe_subscription.status,
                trial_period_days,
                billing_interval,
                user_id
            )

            logger.info("Tenant-independent subscription record created after SetupIntent",
                       subscription_id=stripe_subscription.id,
                       user_id=user_id)

            # Convert current_period_end to ISO format
            current_period_end = stripe_subscription.current_period_end
            if isinstance(current_period_end, int):
                current_period_end = datetime.fromtimestamp(current_period_end, tz=timezone.utc).isoformat()
            elif hasattr(current_period_end, 'isoformat'):
                current_period_end = current_period_end.isoformat()
            else:
                current_period_end = str(current_period_end)

            return {
                "success": True,
                "customer_id": customer_id,
                "subscription_id": stripe_subscription.id,
                "status": stripe_subscription.status,
                "plan": plan_id,
                "billing_cycle": billing_interval,
                "trial_period_days": trial_period_days,
                "current_period_end": current_period_end,
                "user_id": user_id,
                "setup_intent_id": setup_intent_id
            }

        except Exception as e:
            logger.error("Failed to complete subscription after SetupIntent",
                       error=str(e),
                       setup_intent_id=setup_intent_id,
                       user_id=user_id)
            raise DatabaseError(f"Failed to complete subscription: {str(e)}")

    async def orchestrate_subscription_cancellation(
        self,
        tenant_id: str,
        reason: str = ""
    ) -> Dict[str, Any]:
        """
        Orchestrate the complete subscription cancellation workflow

        Args:
            tenant_id: Tenant ID to cancel subscription for
            reason: Optional cancellation reason

        Returns:
            Dictionary with cancellation details
        """
        try:
            logger.info("Starting subscription cancellation orchestration",
                       tenant_id=tenant_id, reason=reason)

            # Step 1: Cancel in subscription service (database status update)
            cancellation_result = await self.subscription_service.cancel_subscription(
                tenant_id, reason
            )

            logger.info("Subscription cancelled in database",
                       tenant_id=tenant_id,
                       status=cancellation_result["status"])

            # Step 2: Get the subscription to find Stripe subscription ID
            subscription = await self.subscription_service.get_subscription_by_tenant_id(tenant_id)

            if subscription and subscription.subscription_id:
                # Step 3: Cancel in payment provider
                stripe_subscription = await self.payment_service.cancel_payment_subscription(
                    subscription.subscription_id
                )

                logger.info("Subscription cancelled in payment provider",
                           subscription_id=stripe_subscription.id,
                           stripe_status=stripe_subscription.status)

                # Step 4: Sync status back to database
                await self.subscription_service.update_subscription_status(
                    tenant_id,
                    stripe_subscription.status,
                    {
                        'current_period_end': stripe_subscription.current_period_end
                    }
                )

            # Step 5: Update tenant status
            tenant_update_data = {
                'subscription_status': 'pending_cancellation',
                'subscription_cancelled_at': datetime.now(timezone.utc)
            }

            await self.tenant_service.update_tenant_subscription_info(
                tenant_id, tenant_update_data
            )

            logger.info("Tenant subscription status updated",
                       tenant_id=tenant_id)

            return cancellation_result

        except ValidationError as ve:
            logger.error("Subscription cancellation validation failed",
                       error=str(ve), tenant_id=tenant_id)
            raise ve

        except Exception as e:
            logger.error("Subscription cancellation orchestration failed",
                       error=str(e), tenant_id=tenant_id)
            raise DatabaseError(f"Failed to cancel subscription: {str(e)}")

    async def orchestrate_subscription_reactivation(
        self,
        tenant_id: str,
        plan: str = "starter"
    ) -> Dict[str, Any]:
        """
        Orchestrate subscription reactivation workflow

        Args:
            tenant_id: Tenant ID to reactivate
            plan: Plan to reactivate with

        Returns:
            Dictionary with reactivation details
        """
        try:
            logger.info("Starting subscription reactivation orchestration",
                       tenant_id=tenant_id, plan=plan)

            # Step 1: Reactivate in subscription service
            reactivation_result = await self.subscription_service.reactivate_subscription(
                tenant_id, plan
            )

            logger.info("Subscription reactivated in database",
                       tenant_id=tenant_id,
                       new_plan=plan)

            # Step 2: Update tenant status
            tenant_update_data = {
                'subscription_status': 'active',
                'subscription_plan': plan,
                'subscription_tier': plan,
                'subscription_cancelled_at': None
            }

            await self.tenant_service.update_tenant_subscription_info(
                tenant_id, tenant_update_data
            )

            logger.info("Tenant subscription status updated after reactivation",
                       tenant_id=tenant_id)

            return reactivation_result

        except ValidationError as ve:
            logger.error("Subscription reactivation validation failed",
                       error=str(ve), tenant_id=tenant_id)
            raise ve

        except Exception as e:
            logger.error("Subscription reactivation orchestration failed",
                       error=str(e), tenant_id=tenant_id)
            raise DatabaseError(f"Failed to reactivate subscription: {str(e)}")

    async def orchestrate_plan_upgrade(
        self,
        tenant_id: str,
        new_plan: str,
        proration_behavior: str = "create_prorations",
        immediate_change: bool = False,
        billing_cycle: str = "monthly"
    ) -> Dict[str, Any]:
        """
        Orchestrate plan upgrade workflow with proration and trial preservation

        Args:
            tenant_id: Tenant ID
            new_plan: New plan name
            proration_behavior: Proration behavior ('create_prorations', 'none', 'always_invoice')
            immediate_change: Whether to apply changes immediately
            billing_cycle: Billing cycle for new plan

        Returns:
            Dictionary with upgrade results

        Trial Handling:
            - If subscription is in trial, the trial period is preserved
            - No proration charges are created during trial
            - After trial ends, the user is charged at the new tier price
        """
        try:
            logger.info("Starting plan upgrade orchestration",
                       tenant_id=tenant_id,
                       new_plan=new_plan,
                       immediate_change=immediate_change)

            # Step 1: Get current subscription
            subscription = await self.subscription_service.get_subscription_by_tenant_id(tenant_id)

            if not subscription:
                raise ValidationError(f"Subscription not found for tenant {tenant_id}")

            if not subscription.subscription_id:
                raise ValidationError(f"Tenant {tenant_id} does not have a Stripe subscription ID")

            # Step 1.5: Check if subscription is in trial
            is_trialing = subscription.status == 'trialing'
            trial_ends_at = subscription.trial_ends_at

            logger.info("Subscription trial status",
                       tenant_id=tenant_id,
                       is_trialing=is_trialing,
                       trial_ends_at=str(trial_ends_at) if trial_ends_at else None,
                       current_plan=subscription.plan)

            # For trial subscriptions:
            # - No proration charges (proration_behavior='none')
            # - Preserve trial period
            # - User gets new tier features immediately
            # - User is charged new tier price when trial ends
            if is_trialing:
                proration_behavior = "none"
                logger.info("Trial subscription detected, disabling proration",
                           tenant_id=tenant_id)

            # Step 2: Get Stripe price ID for new plan
            stripe_price_id = self.payment_service._get_stripe_price_id(new_plan, billing_cycle)

            # Step 3: Calculate proration preview (only if not trialing)
            proration_details = {}
            if not is_trialing:
                proration_details = await self.payment_service.calculate_payment_proration(
                    subscription.subscription_id,
                    stripe_price_id,
                    proration_behavior
                )
                logger.info("Proration calculated for plan upgrade",
                           tenant_id=tenant_id,
                           proration_amount=proration_details.get("net_amount", 0))
            else:
                proration_details = {
                    "subscription_id": subscription.subscription_id,
                    "new_price_id": stripe_price_id,
                    "proration_behavior": proration_behavior,
                    "net_amount": 0,
                    "trial_preserved": True
                }

            # Step 4: Update in payment provider with trial preservation
            updated_stripe_subscription = await self.payment_service.update_payment_subscription(
                subscription.subscription_id,
                stripe_price_id,
                proration_behavior=proration_behavior,
                billing_cycle_anchor="now" if immediate_change and not is_trialing else "unchanged",
                payment_behavior="error_if_incomplete",
                immediate_change=immediate_change,
                preserve_trial=is_trialing  # Preserve trial if currently trialing
            )

            logger.info("Plan updated in payment provider",
                       subscription_id=updated_stripe_subscription.id,
                       new_status=updated_stripe_subscription.status,
                       is_trialing=getattr(updated_stripe_subscription, 'is_trialing', False))

            # Step 5: Update local subscription record
            update_result = await self.subscription_service.update_subscription_plan_record(
                tenant_id,
                new_plan,
                updated_stripe_subscription.status,
                updated_stripe_subscription.current_period_start,
                updated_stripe_subscription.current_period_end,
                billing_cycle,
                proration_details
            )

            logger.info("Local subscription record updated",
                       tenant_id=tenant_id,
                       new_plan=new_plan)

            # Step 6: Update tenant with new plan information
            tenant_update_data = {
                'subscription_plan': new_plan,
                'subscription_tier': new_plan,
                'billing_cycle': billing_cycle
            }

            await self.tenant_service.update_tenant_subscription_info(
                tenant_id, tenant_update_data
            )

            logger.info("Tenant plan information updated",
                       tenant_id=tenant_id)

            # Add upgrade metadata to result
            update_result["immediate_change"] = immediate_change
            update_result["is_trialing"] = is_trialing
            update_result["trial_preserved"] = is_trialing
            if trial_ends_at:
                update_result["trial_ends_at"] = trial_ends_at.isoformat()

            return update_result

        except ValidationError as ve:
            logger.error("Plan upgrade validation failed",
                       error=str(ve), tenant_id=tenant_id)
            raise ve

        except Exception as e:
            logger.error("Plan upgrade orchestration failed",
                       error=str(e), tenant_id=tenant_id)
            raise DatabaseError(f"Failed to upgrade plan: {str(e)}")

    async def orchestrate_billing_cycle_change(
        self,
        tenant_id: str,
        new_billing_cycle: str,
        proration_behavior: str = "create_prorations"
    ) -> Dict[str, Any]:
        """
        Orchestrate billing cycle change workflow

        Args:
            tenant_id: Tenant ID
            new_billing_cycle: New billing cycle (monthly/yearly)
            proration_behavior: Proration behavior

        Returns:
            Dictionary with billing cycle change results
        """
        try:
            logger.info("Starting billing cycle change orchestration",
                       tenant_id=tenant_id,
                       new_billing_cycle=new_billing_cycle)

            # Step 1: Get current subscription
            subscription = await self.subscription_service.get_subscription_by_tenant_id(tenant_id)

            if not subscription:
                raise ValidationError(f"Subscription not found for tenant {tenant_id}")

            if not subscription.subscription_id:
                raise ValidationError(f"Tenant {tenant_id} does not have a Stripe subscription ID")

            # Step 2: Change billing cycle in payment provider
            updated_stripe_subscription = await self.payment_service.change_billing_cycle(
                subscription.subscription_id,
                new_billing_cycle,
                proration_behavior
            )

            logger.info("Billing cycle changed in payment provider",
                       subscription_id=updated_stripe_subscription.id,
                       new_billing_cycle=new_billing_cycle)

            # Step 3: Get proration details (if available)
            proration_details = {}  # Billing cycle change returns proration info in subscription object

            # Step 4: Update local subscription record
            update_result = await self.subscription_service.update_billing_cycle_record(
                tenant_id,
                new_billing_cycle,
                updated_stripe_subscription.status,
                updated_stripe_subscription.current_period_start,
                updated_stripe_subscription.current_period_end,
                subscription.plan,  # current_plan
                proration_details
            )

            logger.info("Local subscription record updated with new billing cycle",
                       tenant_id=tenant_id)

            # Step 5: Update tenant with new billing cycle
            tenant_update_data = {
                'billing_cycle': new_billing_cycle
            }

            await self.tenant_service.update_tenant_subscription_info(
                tenant_id, tenant_update_data
            )

            logger.info("Tenant billing cycle information updated",
                       tenant_id=tenant_id)

            return update_result

        except ValidationError as ve:
            logger.error("Billing cycle change validation failed",
                       error=str(ve), tenant_id=tenant_id)
            raise ve

        except Exception as e:
            logger.error("Billing cycle change orchestration failed",
                       error=str(e), tenant_id=tenant_id)
            raise DatabaseError(f"Failed to change billing cycle: {str(e)}")

    async def orchestrate_coupon_redemption(
        self,
        tenant_id: str,
        coupon_code: str,
        base_trial_days: int = 0
    ) -> Dict[str, Any]:
        """
        Orchestrate coupon redemption workflow

        Args:
            tenant_id: Tenant ID
            coupon_code: Coupon code to redeem
            base_trial_days: Base trial days without coupon

        Returns:
            Dictionary with redemption results
        """
        try:
            logger.info("Starting coupon redemption orchestration",
                       tenant_id=tenant_id,
                       coupon_code=coupon_code)

            # Note: CouponService requires sync session
            # This needs to be refactored to work with async properly
            # For now, return a simplified response
            logger.warning("Coupon redemption not fully implemented in orchestration service",
                          tenant_id=tenant_id,
                          coupon_code=coupon_code)

            return {
                "success": False,
                "error": "Coupon redemption requires session refactoring",
                "coupon_valid": False
            }

        except Exception as e:
            logger.error("Coupon redemption orchestration failed",
                       error=str(e),
                       tenant_id=tenant_id,
                       coupon_code=coupon_code)
            raise DatabaseError(f"Failed to redeem coupon: {str(e)}")

    async def handle_payment_webhook(
        self,
        event_type: str,
        event_data: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Handle payment provider webhook events

        Args:
            event_type: Webhook event type
            event_data: Webhook event data

        Returns:
            Dictionary with webhook processing results
        """
        try:
            logger.info("Processing payment webhook event",
                       event_type=event_type,
                       event_id=event_data.get('id'))

            result = {
                "event_type": event_type,
                "processed": True,
                "actions_taken": []
            }

            # Handle different event types
            if event_type == 'customer.subscription.created':
                await self._handle_subscription_created(event_data)
                result["actions_taken"].append("subscription_created")

            elif event_type == 'customer.subscription.updated':
                await self._handle_subscription_updated(event_data)
                result["actions_taken"].append("subscription_updated")

            elif event_type == 'customer.subscription.deleted':
                await self._handle_subscription_deleted(event_data)
                result["actions_taken"].append("subscription_deleted")

            elif event_type == 'invoice.payment_succeeded':
                await self._handle_payment_succeeded(event_data)
                result["actions_taken"].append("payment_succeeded")

            elif event_type == 'invoice.payment_failed':
                await self._handle_payment_failed(event_data)
                result["actions_taken"].append("payment_failed")

            elif event_type == 'customer.subscription.trial_will_end':
                await self._handle_trial_will_end(event_data)
                result["actions_taken"].append("trial_will_end")

            elif event_type == 'invoice.payment_action_required':
                await self._handle_payment_action_required(event_data)
                result["actions_taken"].append("payment_action_required")

            elif event_type == 'customer.subscription.paused':
                await self._handle_subscription_paused(event_data)
                result["actions_taken"].append("subscription_paused")

            elif event_type == 'customer.subscription.resumed':
                await self._handle_subscription_resumed(event_data)
                result["actions_taken"].append("subscription_resumed")

            elif event_type == 'payment_intent.succeeded':
                await self._handle_payment_intent_succeeded(event_data)
                result["actions_taken"].append("payment_intent_succeeded")

            elif event_type == 'payment_intent.payment_failed':
                await self._handle_payment_intent_failed(event_data)
                result["actions_taken"].append("payment_intent_failed")

            elif event_type == 'payment_intent.requires_action':
                await self._handle_payment_intent_requires_action(event_data)
                result["actions_taken"].append("payment_intent_requires_action")

            elif event_type == 'setup_intent.succeeded':
                await self._handle_setup_intent_succeeded(event_data)
                result["actions_taken"].append("setup_intent_succeeded")

            elif event_type == 'setup_intent.requires_action':
                await self._handle_setup_intent_requires_action(event_data)
                result["actions_taken"].append("setup_intent_requires_action")

            else:
                logger.info("Unhandled webhook event type", event_type=event_type)
                result["processed"] = False

            logger.info("Webhook event processed successfully",
                       event_type=event_type,
                       actions_taken=result["actions_taken"])

            return result

        except Exception as e:
            logger.error("Failed to process webhook event",
                       error=str(e),
                       event_type=event_type,
                       event_id=event_data.get('id'))
            raise DatabaseError(f"Failed to process webhook: {str(e)}")

    async def _handle_subscription_created(self, event_data: Dict[str, Any]):
        """Handle subscription created event"""
        subscription_id = event_data['id']
        customer_id = event_data['customer']
        status = event_data['status']

        logger.info("Handling subscription created event",
                   subscription_id=subscription_id,
                   customer_id=customer_id,
                   status=status)

        # Find tenant by customer ID
        tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

        if tenant:
            # Update subscription status
            await self.subscription_service.update_subscription_status(
                str(tenant.id),
                status,
                {
                    'current_period_start': datetime.fromtimestamp(event_data['current_period_start']),
                    'current_period_end': datetime.fromtimestamp(event_data['current_period_end'])
                }
            )

            # Update tenant status
            tenant_update_data = {
                'subscription_status': status,
                'subscription_created_at': datetime.now(timezone.utc)
            }

            await self.tenant_service.update_tenant_subscription_info(
                str(tenant.id), tenant_update_data
            )

            logger.info("Subscription created event handled",
                       tenant_id=str(tenant.id),
                       subscription_id=subscription_id)

    async def _handle_subscription_updated(self, event_data: Dict[str, Any]):
        """Handle subscription updated event"""
        subscription_id = event_data['id']
        status = event_data['status']

        logger.info("Handling subscription updated event",
                   subscription_id=subscription_id,
                   status=status)

        # Find tenant by subscription
        subscription = await self.subscription_service.get_subscription_by_provider_id(subscription_id)

        if subscription:
            # Update subscription status
            await self.subscription_service.update_subscription_status(
                subscription.tenant_id,
                status,
                {
                    'current_period_start': datetime.fromtimestamp(event_data['current_period_start']),
                    'current_period_end': datetime.fromtimestamp(event_data['current_period_end'])
                }
            )

            # Update tenant status
            tenant_update_data = {
                'subscription_status': status
            }

            await self.tenant_service.update_tenant_subscription_info(
                subscription.tenant_id, tenant_update_data
            )

            logger.info("Subscription updated event handled",
                       tenant_id=subscription.tenant_id,
                       subscription_id=subscription_id)

    async def _handle_subscription_deleted(self, event_data: Dict[str, Any]):
        """Handle subscription deleted event"""
        subscription_id = event_data['id']

        logger.info("Handling subscription deleted event",
                   subscription_id=subscription_id)

        # Find and update subscription
        subscription = await self.subscription_service.get_subscription_by_provider_id(subscription_id)

        if subscription:
            # Cancel subscription in our system
            await self.subscription_service.cancel_subscription(
                subscription.tenant_id,
                "Subscription deleted in payment provider"
            )

            logger.info("Subscription deleted event handled",
                       tenant_id=subscription.tenant_id,
                       subscription_id=subscription_id)

    async def _handle_payment_succeeded(self, event_data: Dict[str, Any]):
        """Handle successful payment event"""
        invoice_id = event_data['id']
        subscription_id = event_data.get('subscription')
        customer_id = event_data['customer']

        logger.info("Handling payment succeeded event",
                   invoice_id=invoice_id,
                   subscription_id=subscription_id,
                   customer_id=customer_id)

        # Find tenant and update payment status
        tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

        if tenant:
            tenant_update_data = {
                'last_payment_date': datetime.now(timezone.utc),
                'payment_status': 'paid'
            }

            await self.tenant_service.update_tenant_subscription_info(
                str(tenant.id), tenant_update_data
            )

            logger.info("Payment succeeded event handled",
                       tenant_id=str(tenant.id),
                       invoice_id=invoice_id)

    async def _handle_payment_failed(self, event_data: Dict[str, Any]):
        """Handle failed payment event"""
        invoice_id = event_data['id']
        subscription_id = event_data.get('subscription')
        customer_id = event_data['customer']

        logger.warning("Handling payment failed event",
                      invoice_id=invoice_id,
                      subscription_id=subscription_id,
                      customer_id=customer_id)

        # Find tenant and update payment status
        tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

        if tenant:
            tenant_update_data = {
                'payment_status': 'failed',
                'last_payment_failure': datetime.now(timezone.utc)
            }

            await self.tenant_service.update_tenant_subscription_info(
                str(tenant.id), tenant_update_data
            )

            logger.info("Payment failed event handled",
                       tenant_id=str(tenant.id),
                       invoice_id=invoice_id)

    async def _handle_trial_will_end(self, event_data: Dict[str, Any]):
        """Handle trial will end event (3 days before trial ends)"""
        subscription_id = event_data['id']
        customer_id = event_data['customer']
        trial_end = event_data.get('trial_end')

        logger.info("Handling trial will end event",
                   subscription_id=subscription_id,
                   customer_id=customer_id,
                   trial_end=trial_end)

        tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

        if tenant:
            tenant_update_data = {
                'trial_ending_soon': True,
                'trial_end_notified_at': datetime.now(timezone.utc)
            }

            await self.tenant_service.update_tenant_subscription_info(
                str(tenant.id), tenant_update_data
            )

            logger.info("Trial will end event handled",
                       tenant_id=str(tenant.id),
                       subscription_id=subscription_id)

    async def _handle_payment_action_required(self, event_data: Dict[str, Any]):
        """Handle payment action required event (3D Secure, etc.)"""
        invoice_id = event_data['id']
        customer_id = event_data['customer']
        subscription_id = event_data.get('subscription')

        logger.info("Handling payment action required event",
                   invoice_id=invoice_id,
                   customer_id=customer_id,
                   subscription_id=subscription_id)

        tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

        if tenant:
            tenant_update_data = {
                'payment_action_required': True,
                'last_payment_action_required_at': datetime.now(timezone.utc)
            }

            await self.tenant_service.update_tenant_subscription_info(
                str(tenant.id), tenant_update_data
            )

            logger.info("Payment action required event handled",
                       tenant_id=str(tenant.id),
                       invoice_id=invoice_id)

    async def _handle_subscription_paused(self, event_data: Dict[str, Any]):
        """Handle subscription paused event"""
        subscription_id = event_data['id']
        customer_id = event_data['customer']
        status = 'paused'

        logger.info("Handling subscription paused event",
                   subscription_id=subscription_id,
                   customer_id=customer_id)

        tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

        if tenant:
            await self.subscription_service.update_subscription_status(
                str(tenant.id),
                status,
                {
                    'paused_at': datetime.now(timezone.utc)
                }
            )

            tenant_update_data = {
                'subscription_status': status,
                'paused_at': datetime.now(timezone.utc)
            }

            await self.tenant_service.update_tenant_subscription_info(
                str(tenant.id), tenant_update_data
            )

            logger.info("Subscription paused event handled",
                       tenant_id=str(tenant.id),
                       subscription_id=subscription_id)

    async def _handle_subscription_resumed(self, event_data: Dict[str, Any]):
        """Handle subscription resumed event"""
        subscription_id = event_data['id']
        customer_id = event_data['customer']
        status = event_data['status']

        logger.info("Handling subscription resumed event",
                   subscription_id=subscription_id,
                   customer_id=customer_id)

        tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

        if tenant:
            await self.subscription_service.update_subscription_status(
                str(tenant.id),
                status,
                {
                    'resumed_at': datetime.now(timezone.utc)
                }
            )

            tenant_update_data = {
                'subscription_status': status,
                'resumed_at': datetime.now(timezone.utc)
            }

            await self.tenant_service.update_tenant_subscription_info(
                str(tenant.id), tenant_update_data
            )

            logger.info("Subscription resumed event handled",
                       tenant_id=str(tenant.id),
                       subscription_id=subscription_id)

    async def _handle_payment_intent_succeeded(self, event_data: Dict[str, Any]):
        """Handle payment intent succeeded event (including 3DS authenticated payments)"""
        payment_intent_id = event_data['id']
        customer_id = event_data.get('customer')
        amount = event_data.get('amount', 0) / 100.0
        currency = event_data.get('currency', 'eur').upper()

        logger.info("Handling payment intent succeeded event",
                   payment_intent_id=payment_intent_id,
                   customer_id=customer_id,
                   amount=amount)

        if customer_id:
            tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

            if tenant:
                tenant_update_data = {
                    'payment_action_required': False,
                    'last_successful_payment_at': datetime.now(timezone.utc)
                }

                await self.tenant_service.update_tenant_subscription_info(
                    str(tenant.id), tenant_update_data
                )

                logger.info("Payment intent succeeded event handled",
                           tenant_id=str(tenant.id),
                           payment_intent_id=payment_intent_id)

    async def _handle_payment_intent_failed(self, event_data: Dict[str, Any]):
        """Handle payment intent failed event (including 3DS authentication failures)"""
        payment_intent_id = event_data['id']
        customer_id = event_data.get('customer')
        last_payment_error = event_data.get('last_payment_error', {})
        error_message = last_payment_error.get('message', 'Payment failed')

        logger.warning("Handling payment intent failed event",
                      payment_intent_id=payment_intent_id,
                      customer_id=customer_id,
                      error_message=error_message)

        if customer_id:
            tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

            if tenant:
                tenant_update_data = {
                    'payment_action_required': False,
                    'last_payment_failure_at': datetime.now(timezone.utc),
                    'last_payment_error': error_message
                }

                await self.tenant_service.update_tenant_subscription_info(
                    str(tenant.id), tenant_update_data
                )

                logger.info("Payment intent failed event handled",
                           tenant_id=str(tenant.id),
                           payment_intent_id=payment_intent_id)

    async def _handle_payment_intent_requires_action(self, event_data: Dict[str, Any]):
        """Handle payment intent requires action event (3DS authentication needed)"""
        payment_intent_id = event_data['id']
        customer_id = event_data.get('customer')
        next_action = event_data.get('next_action', {})
        action_type = next_action.get('type', 'unknown')

        logger.info("Handling payment intent requires action event",
                   payment_intent_id=payment_intent_id,
                   customer_id=customer_id,
                   action_type=action_type)

        if customer_id:
            tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

            if tenant:
                tenant_update_data = {
                    'payment_action_required': True,
                    'payment_action_type': action_type,
                    'last_payment_action_required_at': datetime.now(timezone.utc)
                }

                await self.tenant_service.update_tenant_subscription_info(
                    str(tenant.id), tenant_update_data
                )

                logger.info("Payment intent requires action event handled",
                           tenant_id=str(tenant.id),
                           payment_intent_id=payment_intent_id,
                           action_type=action_type)

    async def _handle_setup_intent_succeeded(self, event_data: Dict[str, Any]):
        """Handle setup intent succeeded event (3DS authentication completed)"""
        setup_intent_id = event_data['id']
        customer_id = event_data.get('customer')
        
        logger.info("Handling setup intent succeeded event (3DS authentication completed)",
                   setup_intent_id=setup_intent_id,
                   customer_id=customer_id)

        if customer_id:
            tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

            if tenant:
                tenant_update_data = {
                    'threeds_authentication_completed': True,
                    'threeds_authentication_completed_at': datetime.now(timezone.utc),
                    'last_threeds_setup_intent_id': setup_intent_id
                }

                await self.tenant_service.update_tenant_subscription_info(
                    str(tenant.id), tenant_update_data
                )

                logger.info("Setup intent succeeded event handled (3DS authentication completed)",
                           tenant_id=str(tenant.id),
                           setup_intent_id=setup_intent_id)

    async def _handle_setup_intent_requires_action(self, event_data: Dict[str, Any]):
        """Handle setup intent requires action event (3DS authentication needed)"""
        setup_intent_id = event_data['id']
        customer_id = event_data.get('customer')
        next_action = event_data.get('next_action', {})
        action_type = next_action.get('type', 'unknown')
        
        logger.info("Handling setup intent requires action event (3DS authentication needed)",
                   setup_intent_id=setup_intent_id,
                   customer_id=customer_id,
                   action_type=action_type)

        if customer_id:
            tenant = await self.tenant_service.get_tenant_by_customer_id(customer_id)

            if tenant:
                tenant_update_data = {
                    'threeds_authentication_required': True,
                    'threeds_authentication_required_at': datetime.now(timezone.utc),
                    'last_threeds_setup_intent_id': setup_intent_id,
                    'threeds_action_type': action_type
                }

                await self.tenant_service.update_tenant_subscription_info(
                    str(tenant.id), tenant_update_data
                )

                logger.info("Setup intent requires action event handled (3DS authentication needed)",
                           tenant_id=str(tenant.id),
                           setup_intent_id=setup_intent_id,
                           action_type=action_type)

    async def orchestrate_subscription_creation_with_default_payment(
        self,
        tenant_id: str,
        user_data: Dict[str, Any],
        plan_id: str,
        billing_interval: str = "monthly",
        coupon_code: Optional[str] = None,
        payment_method_id: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Orchestrate subscription creation using user's default payment method if available
        
        This method tries to use the user's default payment method from auth service
        if no payment_method_id is provided. Falls back to manual payment entry if needed.
        
        Args:
            tenant_id: Tenant ID
            user_data: User data for customer creation
            plan_id: Subscription plan ID
            billing_interval: Billing interval (monthly/yearly)
            coupon_code: Optional coupon code
            payment_method_id: Optional payment method ID (if not provided, tries to fetch default)
            
        Returns:
            Dictionary with subscription creation results
        """
        try:
            logger.info("Starting subscription creation with default payment method",
                       tenant_id=tenant_id, plan_id=plan_id)

            # Step 0: Try to get user's default payment method if not provided
            if not payment_method_id:
                payment_method_id = await self._get_user_default_payment_method(user_data.get('user_id'))
                
                if payment_method_id:
                    logger.info("Using user's default payment method for subscription",
                               tenant_id=tenant_id,
                               payment_method_id=payment_method_id)
                else:
                    logger.info("No default payment method found for user, will create subscription without attached payment method",
                               tenant_id=tenant_id,
                               user_id=user_data.get('user_id'))

            # Step 1: Create subscription using the existing orchestration method
            result = await self.orchestrate_subscription_creation(
                tenant_id,
                user_data,
                plan_id,
                payment_method_id if payment_method_id else '',
                billing_interval,
                coupon_code
            )

            logger.info("Subscription creation with default payment completed successfully",
                       tenant_id=tenant_id,
                       subscription_id=result.get('subscription', {}).get('id'))

            return result

        except Exception as e:
            logger.error("Subscription creation with default payment failed",
                        error=str(e),
                        tenant_id=tenant_id,
                        plan_id=plan_id)
            raise e

    async def _get_user_default_payment_method(self, user_id: Optional[str]) -> Optional[str]:
        """
        Get user's default payment method from auth service
        
        Args:
            user_id: User ID to fetch payment method for
            
        Returns:
            Payment method ID if found, None otherwise
        """
        if not user_id:
            logger.warning("Cannot fetch default payment method - no user_id provided")
            return None
            
        try:
            from app.core.config import settings
            from shared.clients.auth_client import AuthServiceClient
            
            auth_client = AuthServiceClient(settings)
            user_data = await auth_client.get_user_details(user_id)
            
            if user_data and user_data.get('default_payment_method_id'):
                logger.info("Retrieved user's default payment method from auth service",
                           user_id=user_id,
                           payment_method_id=user_data['default_payment_method_id'])
                return user_data['default_payment_method_id']
            else:
                logger.info("No default payment method found for user in auth service",
                           user_id=user_id)
                return None
                
        except Exception as e:
            logger.warning("Failed to retrieve user's default payment method from auth service",
                          user_id=user_id,
                          error=str(e))
            # Don't fail the subscription creation if we can't get the default payment method
            return None

    async def get_payment_method(self, tenant_id: str) -> Optional[Dict[str, Any]]:
        """
        Get the current payment method for a tenant's subscription

        This is an orchestration method that coordinates between:
        1. SubscriptionService (to get subscription data)
        2. PaymentService (to get payment method from provider)

        Args:
            tenant_id: Tenant ID

        Returns:
            Dictionary with payment method details or None
        """
        try:
            # Get subscription from database
            subscription = await self.subscription_service.get_subscription_by_tenant_id(tenant_id)

            if not subscription:
                logger.warning("get_payment_method_no_subscription",
                             tenant_id=tenant_id)
                return None

            # Check if subscription has a customer ID
            if not subscription.customer_id:
                logger.warning("get_payment_method_no_customer_id",
                             tenant_id=tenant_id)
                return None

            # Get payment method from payment provider
            payment_method = await self.payment_service.get_customer_payment_method(subscription.customer_id)

            if not payment_method:
                logger.info("get_payment_method_not_found",
                          tenant_id=tenant_id,
                          customer_id=subscription.customer_id)
                return None

            logger.info("payment_method_retrieved",
                       tenant_id=tenant_id,
                       payment_method_type=payment_method.type,
                       last4=payment_method.last4)

            return {
                "brand": payment_method.brand,
                "last4": payment_method.last4,
                "exp_month": payment_method.exp_month,
                "exp_year": payment_method.exp_year
            }

        except Exception as e:
            logger.error("get_payment_method_failed",
                       error=str(e),
                       tenant_id=tenant_id)
            return None

    async def get_invoices(self, tenant_id: str) -> Dict[str, Any]:
        """
        Get invoice history for a tenant's subscription

        This is an orchestration method that coordinates between:
        1. SubscriptionService (to get subscription data)
        2. PaymentService (to get invoices from provider)

        Args:
            tenant_id: Tenant ID

        Returns:
            Dictionary with invoices data
        """
        try:
            # Get subscription from database
            subscription = await self.subscription_service.get_subscription_by_tenant_id(tenant_id)

            if not subscription:
                logger.warning("get_invoices_no_subscription",
                             tenant_id=tenant_id)
                return {"invoices": []}

            # Check if subscription has a customer ID
            if not subscription.customer_id:
                logger.warning("get_invoices_no_customer_id",
                             tenant_id=tenant_id)
                return {"invoices": []}

            # Get invoices from payment provider
            invoices_result = await self.payment_service.stripe_client.get_invoices(subscription.customer_id)

            logger.info("invoices_retrieved",
                       tenant_id=tenant_id,
                       customer_id=subscription.customer_id,
                       invoice_count=len(invoices_result.get("invoices", [])))

            return invoices_result

        except Exception as e:
            logger.error("get_invoices_failed",
                       error=str(e),
                       tenant_id=tenant_id,
                       exc_info=True)
            return {"invoices": []}

    async def update_payment_method(
        self,
        tenant_id: str,
        payment_method_id: str
    ) -> Dict[str, Any]:
        """
        Update the default payment method for a tenant's subscription

        This is an orchestration method that coordinates between:
        1. SubscriptionService (to get subscription data)
        2. PaymentService (to update payment method with provider)

        Args:
            tenant_id: Tenant ID
            payment_method_id: New payment method ID from frontend

        Returns:
            Dictionary with updated payment method details

        Raises:
            ValidationError: If subscription or customer_id not found
        """
        try:
            # Get subscription from database
            subscription = await self.subscription_service.get_subscription_by_tenant_id(tenant_id)

            if not subscription:
                raise ValidationError(f"Subscription not found for tenant {tenant_id}")

            if not subscription.customer_id:
                raise ValidationError(f"Tenant {tenant_id} does not have a payment customer ID")

            # Update payment method via payment provider
            payment_result = await self.payment_service.update_payment_method(
                subscription.customer_id,
                payment_method_id
            )

            logger.info("payment_method_updated",
                       tenant_id=tenant_id,
                       payment_method_id=payment_method_id,
                       requires_action=payment_result.get('requires_action', False))

            pm_details = payment_result.get('payment_method', {})

            return {
                "success": True,
                "message": "Payment method updated successfully",
                "payment_method_id": pm_details.get('id'),
                "brand": pm_details.get('brand', 'unknown'),
                "last4": pm_details.get('last4', '0000'),
                "exp_month": pm_details.get('exp_month'),
                "exp_year": pm_details.get('exp_year'),
                "requires_action": payment_result.get('requires_action', False),
                "client_secret": payment_result.get('client_secret'),
                "payment_intent_status": payment_result.get('payment_intent_status')
            }

        except ValidationError:
            raise
        except Exception as e:
            logger.error("update_payment_method_failed",
                       error=str(e),
                       tenant_id=tenant_id)
            raise DatabaseError(f"Failed to update payment method: {str(e)}")

    async def create_registration_payment_setup(
        self,
        user_data: Dict[str, Any],
        plan_id: str,
        payment_method_id: str,
        billing_interval: str = "monthly",
        coupon_code: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Create payment customer and SetupIntent for registration.

        NEW ARCHITECTURE: Only creates customer + SetupIntent here.
        Subscription is created AFTER SetupIntent verification completes.

        Flow:
        1. Create Stripe customer
        2. Handle coupon (get trial days)
        3. Create SetupIntent for payment verification
        4. Return SetupIntent to frontend for 3DS handling
        5. (Later) complete_registration_subscription() creates subscription

        Args:
            user_data: User data (email, full_name, etc.)
            plan_id: Subscription plan ID
            payment_method_id: Payment method ID from frontend
            billing_interval: Billing interval (monthly/yearly)
            coupon_code: Optional coupon code

        Returns:
            Dictionary with SetupIntent data for frontend

        Raises:
            Exception: If payment setup fails
        """
        try:
            logger.info("Starting registration payment setup",
                       email=user_data.get('email'),
                       plan_id=plan_id)

            # Step 1: Create payment customer
            email = user_data.get('email')
            name = user_data.get('full_name')
            metadata = {
                'registration_flow': 'setup_intent_first',
                'plan_id': plan_id,
                'billing_interval': billing_interval,
                'timestamp': datetime.now(timezone.utc).isoformat()
            }

            customer = await self.payment_service.create_customer(email, name, metadata)
            logger.info("Customer created for registration",
                       customer_id=customer.id,
                       email=email)

            # Step 2: Handle coupon logic (if provided)
            trial_period_days = 0

            if coupon_code:
                logger.info("Validating coupon for registration",
                           coupon_code=coupon_code,
                           email=email)

                coupon_service = CouponService(self.db_session)
                success, discount_applied, error = await coupon_service.redeem_coupon(
                    coupon_code,
                    None,  # No tenant_id yet
                    base_trial_days=0
                )

                if success and discount_applied:
                    trial_period_days = discount_applied.get("total_trial_days", 0)
                    logger.info("Coupon validated for registration",
                               coupon_code=coupon_code,
                               trial_period_days=trial_period_days)
                else:
                    logger.warning("Failed to validate coupon, continuing without it",
                                 coupon_code=coupon_code,
                                 error=error)

            # Step 3: Create SetupIntent (NO subscription yet!)
            logger.info("Creating SetupIntent for registration",
                       customer_id=customer.id,
                       payment_method_id=payment_method_id)

            setup_result = await self.payment_service.create_setup_intent_for_registration(
                customer.id,
                payment_method_id,
                {
                    'purpose': 'registration',
                    'plan_id': plan_id,
                    'billing_interval': billing_interval,
                    'trial_period_days': str(trial_period_days),
                    'coupon_code': coupon_code or ''
                }
            )

            logger.info("SetupIntent created for registration",
                       setup_intent_id=setup_result.get('setup_intent_id'),
                       requires_action=setup_result.get('requires_action'),
                       status=setup_result.get('status'))

            # Return result for frontend
            # Frontend will call complete_registration_subscription() after 3DS
            return {
                "requires_action": setup_result.get('requires_action', True),
                "action_type": "use_stripe_sdk",
                "client_secret": setup_result.get('client_secret'),
                "setup_intent_id": setup_result.get('setup_intent_id'),
                "customer_id": customer.id,
                "payment_customer_id": customer.id,
                "plan_id": plan_id,
                "payment_method_id": payment_method_id,
                "trial_period_days": trial_period_days,
                "billing_interval": billing_interval,
                "coupon_code": coupon_code,
                "email": email,
                "full_name": name,
                "message": "Payment verification required" if setup_result.get('requires_action') else "Payment verified"
            }

        except Exception as e:
            logger.error("Registration payment setup failed",
                        email=user_data.get('email'),
                        error=str(e),
                        exc_info=True)
            raise

    async def verify_setup_intent_for_registration(
        self,
        setup_intent_id: str
    ) -> Dict[str, Any]:
        """
        Verify SetupIntent status for registration completion.

        Args:
            setup_intent_id: SetupIntent ID to verify

        Returns:
            Dictionary with SetupIntent verification result

        Raises:
            Exception: If verification fails
        """
        try:
            logger.info("Verifying SetupIntent for registration",
                       setup_intent_id=setup_intent_id)

            verification_result = await self.payment_service.verify_setup_intent(setup_intent_id)

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

            return verification_result

        except Exception as e:
            logger.error("SetupIntent verification failed",
                        setup_intent_id=setup_intent_id,
                        error=str(e),
                        exc_info=True)
            raise

    async def complete_registration_subscription(
        self,
        setup_intent_id: str,
        customer_id: str,
        plan_id: str,
        payment_method_id: str,
        billing_interval: str = "monthly",
        trial_period_days: int = 0,
        user_id: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Create subscription AFTER SetupIntent verification succeeds.

        NEW ARCHITECTURE: This is called AFTER 3DS verification completes.
        The subscription is created here, not during payment setup.

        Args:
            setup_intent_id: Verified SetupIntent ID
            customer_id: Stripe customer ID
            plan_id: Subscription plan ID
            payment_method_id: Verified payment method ID
            billing_interval: Billing interval (monthly/yearly)
            trial_period_days: Trial period in days (from coupon)
            user_id: Optional user ID if user already created

        Returns:
            Dictionary with subscription details

        Raises:
            Exception: If subscription creation fails
        """
        try:
            logger.info("Creating subscription after verification",
                       setup_intent_id=setup_intent_id,
                       customer_id=customer_id,
                       plan_id=plan_id,
                       trial_period_days=trial_period_days)

            # Verify SetupIntent is successful
            verification = await self.payment_service.verify_setup_intent(setup_intent_id)
            if not verification.get('verified'):
                raise ValidationError(
                    f"SetupIntent not verified. Status: {verification.get('status')}"
                )

            # Get actual customer_id and payment_method_id from verification
            actual_customer_id = verification.get('customer_id') or customer_id
            actual_payment_method_id = verification.get('payment_method_id') or payment_method_id

            # Get price ID for the plan
            price_id = self.payment_service._get_stripe_price_id(plan_id, billing_interval)

            # Create subscription in Stripe
            subscription_result = await self.payment_service.create_subscription_after_verification(
                actual_customer_id,
                price_id,
                actual_payment_method_id,
                trial_period_days if trial_period_days > 0 else None,
                {
                    'plan_id': plan_id,
                    'billing_interval': billing_interval,
                    'created_via': 'registration_flow',
                    'setup_intent_id': setup_intent_id
                }
            )

            logger.info("Subscription created after verification",
                       subscription_id=subscription_result.get('subscription_id'),
                       status=subscription_result.get('status'),
                       trial_period_days=trial_period_days)

            # Create local subscription record (without tenant_id for now)
            subscription_record = await self.subscription_service.create_tenant_independent_subscription_record(
                subscription_result['subscription_id'],
                actual_customer_id,
                plan_id,
                subscription_result['status'],
                trial_period_days,
                billing_interval,
                user_id
            )

            logger.info("Subscription record created",
                       subscription_id=subscription_result['subscription_id'],
                       record_id=str(subscription_record.id) if subscription_record else None)

            return {
                'subscription_id': subscription_result['subscription_id'],
                'customer_id': actual_customer_id,
                'payment_customer_id': actual_customer_id,
                'payment_method_id': actual_payment_method_id,
                'status': subscription_result['status'],
                'plan_id': plan_id,
                'trial_period_days': trial_period_days,
                'current_period_end': subscription_result.get('current_period_end'),
                'message': 'Subscription created successfully'
            }

        except Exception as e:
            logger.error("Subscription creation after verification failed",
                        setup_intent_id=setup_intent_id,
                        customer_id=customer_id,
                        error=str(e),
                        exc_info=True)
            raise

    async def validate_plan_upgrade(
        self,
        tenant_id: str,
        new_plan: str
    ) -> Dict[str, Any]:
        """
        Validate if a tenant can upgrade to a new plan

        Args:
            tenant_id: Tenant ID
            new_plan: New plan to validate upgrade to

        Returns:
            Dictionary with validation result
        """
        try:
            logger.info("Validating plan upgrade",
                       tenant_id=tenant_id,
                       new_plan=new_plan)

            # Delegate to subscription service for validation
            can_upgrade = await self.subscription_service.validate_subscription_change(
                tenant_id,
                new_plan
            )

            result = {
                "can_upgrade": can_upgrade,
                "tenant_id": tenant_id,
                "current_plan": None,  # Would need to fetch current plan if needed
                "new_plan": new_plan
            }

            if not can_upgrade:
                result["reason"] = "Subscription change not allowed based on current status"

            logger.info("Plan upgrade validation completed",
                       tenant_id=tenant_id,
                       can_upgrade=can_upgrade)

            return result

        except Exception as e:
            logger.error("Plan upgrade validation failed",
                        tenant_id=tenant_id,
                        new_plan=new_plan,
                        error=str(e),
                        exc_info=True)
            raise DatabaseError(f"Failed to validate plan upgrade: {str(e)}")

    async def get_subscriptions_by_customer_id(self, customer_id: str) -> List[Subscription]:
        """
        Get all subscriptions for a given customer ID
        
        Args:
            customer_id: Stripe customer ID
            
        Returns:
            List of Subscription objects
        """
        try:
            return await self.subscription_service.get_subscriptions_by_customer_id(customer_id)
        except Exception as e:
            logger.error("Failed to get subscriptions by customer ID",
                        customer_id=customer_id,
                        error=str(e),
                        exc_info=True)
            raise DatabaseError(f"Failed to get subscriptions: {str(e)}")

    async def update_subscription_with_verified_payment(
        self,
        subscription_id: str,
        customer_id: str,
        payment_method_id: str,
        trial_period_days: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        Update an existing trial subscription with a verified payment method
        
        This is used when we already have a trial subscription (created during registration)
        and just need to attach the verified payment method to it after 3DS verification.
        
        For trial subscriptions, the payment method should be:
        1. Attached to the customer (for trial period)
        2. Set as default payment method on the subscription (for future billing)
        
        Args:
            subscription_id: Stripe subscription ID
            customer_id: Stripe customer ID
            payment_method_id: Verified payment method ID
            trial_period_days: Optional trial period (for validation)
            
        Returns:
            Dictionary with updated subscription details
        """
        try:
            logger.info("Updating existing trial subscription with verified payment method",
                       subscription_id=subscription_id,
                       customer_id=customer_id,
                       payment_method_id=payment_method_id,
                       trial_period_days=trial_period_days)
            
            # First, verify the subscription exists and get its current status
            existing_subscription = await self.subscription_service.get_subscription_by_provider_id(subscription_id)
            
            if not existing_subscription:
                raise SubscriptionNotFound(f"Subscription {subscription_id} not found")
            
            # For trial subscriptions, we need to:
            # 1. Ensure payment method is attached to customer
            # 2. Set it as default payment method on subscription
            
            # Step 1: Attach payment method to customer (if not already attached)
            try:
                await self.payment_service.attach_payment_method_to_customer(
                    customer_id,
                    payment_method_id
                )
                logger.info("Payment method attached to customer for trial subscription",
                          customer_id=customer_id,
                          payment_method_id=payment_method_id)
            except Exception as e:
                logger.warning("Payment method may already be attached to customer",
                              customer_id=customer_id,
                              payment_method_id=payment_method_id,
                              error=str(e))
            
            # Step 2: Set payment method as default on subscription
            stripe_subscription = await self.payment_service.update_subscription_payment_method(
                subscription_id,
                payment_method_id
            )
            
            # Step 3: Also set as default payment method on customer for future invoices
            await self.payment_service.set_customer_default_payment_method(
                customer_id,
                payment_method_id
            )
            
            # Update our local subscription record
            await self.subscription_service.update_subscription_status(
                existing_subscription.tenant_id,
                stripe_subscription.status,
                {
                    'current_period_start': datetime.fromtimestamp(stripe_subscription.current_period_start),
                    'current_period_end': datetime.fromtimestamp(stripe_subscription.current_period_end),
                    'payment_method_verified': True,
                    'payment_method_id': payment_method_id
                }
            )
            
            # Create a mock subscription object-like dict for compatibility
            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_id': stripe_subscription.id,
                    'status': stripe_subscription.status,
                    'current_period_start': stripe_subscription.current_period_start,
                    'current_period_end': stripe_subscription.current_period_end,
                    'customer_id': customer_id
                }),
                'verification': {
                    'verified': True,
                    'customer_id': customer_id,
                    'payment_method_id': payment_method_id,
                    'trial_period_days': trial_period_days
                },
                'trial_preserved': True,
                'payment_method_updated': True
            }
            
        except Exception as e:
            logger.error("Failed to update trial subscription with verified payment",
                        subscription_id=subscription_id,
                        customer_id=customer_id,
                        error=str(e),
                        exc_info=True)
            raise SubscriptionUpdateFailed(f"Failed to update trial subscription: {str(e)}")
