"""
Payments App - DRF Views

API endpoints for Stripe payment configuration, Connect accounts, and transactions.
"""
import stripe
from django.conf import settings
from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework import status
from smoothschedule.identity.core.permissions import HasFeaturePermission
from smoothschedule.identity.core.mixins import TenantAPIView, TenantRequiredAPIView, safe_error_message
from decimal import Decimal
from .services import get_stripe_service_for_tenant
from .models import TransactionLink
from smoothschedule.scheduling.schedule.models import Event
from smoothschedule.platform.admin.models import SubscriptionPlan


# ============================================================================
# Helper Functions
# ============================================================================

def mask_key(key):
    """Mask a key showing only first 7 and last 4 characters."""
    if not key:
        return ''
    if len(key) <= 12:
        return '*' * len(key)
    return key[:7] + '*' * (len(key) - 11) + key[-4:]


# ============================================================================
# Payment Configuration Status
# ============================================================================

class PaymentConfigStatusView(TenantRequiredAPIView, APIView):
    """
    Get unified payment configuration status.

    GET /payments/config/status/

    Returns the complete payment setup for the business including:
    - payment_mode: 'none', 'direct_api', or 'connect'
    - tier: subscription tier
    - can_accept_payments: whether payments are enabled
    - api_keys: API key info (if using direct_api mode)
    - connect_account: Connect account info (if using connect mode)
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        tenant = self.tenant

        # Build API keys info if configured
        api_keys = None
        if tenant.payment_mode == 'direct_api' and tenant.stripe_secret_key:
            api_keys = {
                'id': tenant.id,
                'status': tenant.stripe_api_key_status,
                'secret_key_masked': mask_key(tenant.stripe_secret_key),
                'publishable_key_masked': mask_key(tenant.stripe_publishable_key),
                'last_validated_at': tenant.stripe_api_key_validated_at.isoformat() if tenant.stripe_api_key_validated_at else None,
                'stripe_account_id': tenant.stripe_api_key_account_id,
                'stripe_account_name': tenant.stripe_api_key_account_name,
                'validation_error': tenant.stripe_api_key_error,
                'created_at': tenant.created_on.isoformat() if tenant.created_on else None,
                'updated_at': tenant.created_on.isoformat() if tenant.created_on else None,
            }

        # Build Connect account info if configured
        connect_account = None
        if tenant.payment_mode == 'connect' and tenant.stripe_connect_id:
            connect_account = {
                'id': tenant.id,
                'business': tenant.id,
                'business_name': tenant.name,
                'business_subdomain': tenant.schema_name,
                'stripe_account_id': tenant.stripe_connect_id,
                'account_type': 'standard',  # We use standard Connect accounts
                'status': tenant.stripe_connect_status,
                'charges_enabled': tenant.stripe_charges_enabled,
                'payouts_enabled': tenant.stripe_payouts_enabled,
                'details_submitted': tenant.stripe_details_submitted,
                'onboarding_complete': tenant.stripe_onboarding_complete,
                'onboarding_link': None,
                'onboarding_link_expires_at': None,
                'is_onboarding_link_valid': False,
                'created_at': tenant.created_on.isoformat() if tenant.created_on else None,
                'updated_at': tenant.created_on.isoformat() if tenant.created_on else None,
            }

        # Determine if Stripe is configured and ready
        stripe_configured = False
        if tenant.payment_mode == 'direct_api' and tenant.stripe_api_key_status == 'active':
            stripe_configured = True
        elif tenant.payment_mode == 'connect' and tenant.stripe_charges_enabled:
            stripe_configured = True

        # Check if tier/subscription allows payments
        tier_allows_payments = tenant.has_feature('can_accept_payments')

        # Get plan name from billing subscription (with null-safe access)
        plan_name = None
        if hasattr(tenant, 'billing_subscription') and tenant.billing_subscription:
            billing_sub = tenant.billing_subscription
            if billing_sub.plan_version and billing_sub.plan_version.plan:
                plan_name = billing_sub.plan_version.plan.code

        return Response({
            'payment_mode': tenant.payment_mode,
            'plan': plan_name,
            'tier_allows_payments': tier_allows_payments,
            'stripe_configured': stripe_configured,
            'can_accept_payments': stripe_configured and tier_allows_payments,
            'api_keys': api_keys,
            'connect_account': connect_account,
        })


# ============================================================================
# Subscription Plans & Add-ons
# ============================================================================

class SubscriptionPlansView(APIView):
    """
    Get available subscription plans and add-ons for the current business.

    GET /payments/plans/
    Returns plans (base tiers) and available add-ons based on current tier.
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        tenant = request.tenant

        # Get all active, public base plans
        base_plans = SubscriptionPlan.objects.filter(
            is_active=True,
            is_public=True,
            plan_type='base'
        ).order_by('price_monthly')

        # Get all active, public add-ons
        all_addons = SubscriptionPlan.objects.filter(
            is_active=True,
            is_public=True,
            plan_type='addon'
        ).order_by('price_monthly')

        # Filter add-ons based on what the tenant doesn't already have
        # and what's relevant to their tier
        available_addons = []
        for addon in all_addons:
            # Check if the addon provides something the tenant doesn't have
            addon_permissions = addon.permissions or {}

            # Skip if tenant already has this permission from their tier
            skip = False
            for perm_key, perm_value in addon_permissions.items():
                if perm_value and getattr(tenant, perm_key, False):
                    skip = True
                    break

            if not skip:
                available_addons.append(addon)

        def serialize_plan(plan):
            return {
                'id': plan.id,
                'name': plan.name,
                'description': plan.description,
                'plan_type': plan.plan_type,
                'price_monthly': float(plan.price_monthly) if plan.price_monthly else None,
                'price_yearly': float(plan.price_yearly) if plan.price_yearly else None,
                'features': plan.features or [],
                'permissions': plan.permissions or {},
                'limits': plan.limits or {},
                'transaction_fee_percent': float(plan.transaction_fee_percent),
                'transaction_fee_fixed': float(plan.transaction_fee_fixed),
                'is_most_popular': plan.is_most_popular,
                'show_price': plan.show_price,
                'stripe_price_id': plan.stripe_price_id,
            }

        # Determine current plan info from billing subscription (with null-safe access)
        current_plan_name = 'free'
        if hasattr(tenant, 'billing_subscription') and tenant.billing_subscription:
            billing_sub = tenant.billing_subscription
            if billing_sub.plan_version and billing_sub.plan_version.plan:
                current_plan_name = billing_sub.plan_version.plan.code
        # Try to find matching subscription plan by name (case-insensitive)
        current_plan = base_plans.filter(name__iexact=current_plan_name).first()

        return Response({
            'current_plan_name': current_plan_name,
            'current_plan': serialize_plan(current_plan) if current_plan else None,
            'plans': [serialize_plan(p) for p in base_plans],
            'addons': [serialize_plan(a) for a in available_addons],
        })


class CreateCheckoutSessionView(APIView):
    """
    Create a Stripe Checkout session for upgrading or purchasing add-ons.

    POST /payments/checkout/
    Body: { plan_id: number, billing_period: 'monthly' | 'yearly' }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        plan_id = request.data.get('plan_id')
        billing_period = request.data.get('billing_period', 'monthly')

        if not plan_id:
            return Response(
                {'error': 'plan_id is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            plan = SubscriptionPlan.objects.get(id=plan_id, is_active=True)
        except SubscriptionPlan.DoesNotExist:
            return Response(
                {'error': 'Plan not found'},
                status=status.HTTP_404_NOT_FOUND
            )

        if not plan.stripe_price_id:
            return Response(
                {'error': 'This plan is not available for purchase yet'},
                status=status.HTTP_400_BAD_REQUEST
            )

        tenant = request.tenant
        stripe.api_key = settings.STRIPE_SECRET_KEY

        try:
            # Create or get Stripe customer for the tenant
            if not tenant.stripe_customer_id:
                customer = stripe.Customer.create(
                    email=tenant.contact_email or request.user.email,
                    name=tenant.name,
                    metadata={
                        'tenant_id': str(tenant.id),
                        'tenant_schema': tenant.schema_name,
                    }
                )
                tenant.stripe_customer_id = customer.id
                tenant.save()

            # Build success/cancel URLs - point to frontend, not API
            # Use tenant subdomain with frontend port
            frontend_port = '5173'  # Vite dev server
            if settings.DEBUG:
                base_url = f"http://{tenant.schema_name}.lvh.me:{frontend_port}"
            else:
                # In production, use the tenant's primary domain
                base_url = f"https://{tenant.schema_name}.smoothschedule.com"
            success_url = f"{base_url}/settings/billing?checkout=success&session_id={{CHECKOUT_SESSION_ID}}"
            cancel_url = f"{base_url}/settings/billing?checkout=cancelled"

            # Create checkout session
            session = stripe.checkout.Session.create(
                customer=tenant.stripe_customer_id,
                mode='subscription' if plan.plan_type == 'base' else 'subscription',
                line_items=[{
                    'price': plan.stripe_price_id,
                    'quantity': 1,
                }],
                success_url=success_url,
                cancel_url=cancel_url,
                metadata={
                    'tenant_id': str(tenant.id),
                    'tenant_schema': tenant.schema_name,
                    'plan_id': str(plan.id),
                    'plan_type': plan.plan_type,
                },
            )

            return Response({
                'checkout_url': session.url,
                'session_id': session.id,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class SubscriptionsView(APIView):
    """
    Get and manage the tenant's active subscriptions.

    GET /payments/subscriptions/
    Returns list of active subscriptions with their details.
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        from datetime import datetime
        tenant = request.tenant
        stripe.api_key = settings.STRIPE_SECRET_KEY

        if not tenant.stripe_customer_id:
            return Response({
                'subscriptions': [],
                'has_active_subscription': False,
            })

        try:
            # Fetch subscriptions from Stripe
            # Note: Can't expand data.items.data.price.product (too deep)
            # We'll fetch products separately if needed
            subscriptions = stripe.Subscription.list(
                customer=tenant.stripe_customer_id,
                status='all',  # Include active, past_due, canceled, etc.
            )

            result = []
            has_active = False
            for sub in subscriptions.data:
                # Skip fully canceled subscriptions
                if sub.status == 'canceled':
                    continue

                if sub.status in ('active', 'trialing'):
                    has_active = True

                # Get the first item (we typically have one item per subscription)
                # Use bracket notation to avoid conflict with dict.items() method
                items_list = sub['items']['data'] if sub.get('items') else []
                first_item = items_list[0] if items_list else None
                if not first_item:
                    continue

                price = first_item.get('price') or first_item.price
                product_id = price.get('product') if isinstance(price, dict) else price.product

                # Fetch product details separately
                product_name = 'Unknown'
                plan_type = 'base'
                if product_id:
                    try:
                        product = stripe.Product.retrieve(product_id)
                        product_name = product.name or 'Unknown'
                        plan_type = (product.metadata or {}).get('plan_type', 'base')
                    except stripe.error.StripeError:
                        pass

                # Calculate amount - handle both dict and object access
                def get_attr(obj, key, default=None):
                    """Get attribute from dict or object"""
                    if isinstance(obj, dict):
                        return obj.get(key, default)
                    return getattr(obj, key, default)

                unit_amount = get_attr(price, 'unit_amount', 0) or 0
                currency = (get_attr(price, 'currency', 'usd') or 'usd').upper()
                recurring = get_attr(price, 'recurring')
                interval = get_attr(recurring, 'interval', 'month') if recurring else 'month'

                # Format amount for display
                if currency == 'USD':
                    amount_display = f"${unit_amount / 100:.2f}"
                else:
                    amount_display = f"{unit_amount / 100:.2f} {currency}"

                # Get period dates from first_item (new Stripe API) or fall back to sub dates
                period_start = first_item.get('current_period_start') or sub.get('start_date')
                period_end = first_item.get('current_period_end') or sub.get('billing_cycle_anchor')

                result.append({
                    'id': sub.id,
                    'plan_name': product_name,
                    'plan_type': plan_type,
                    'status': sub.status,
                    'current_period_start': datetime.fromtimestamp(period_start).isoformat() if period_start else None,
                    'current_period_end': datetime.fromtimestamp(period_end).isoformat() if period_end else None,
                    'cancel_at_period_end': sub.cancel_at_period_end,
                    'cancel_at': datetime.fromtimestamp(sub.cancel_at).isoformat() if sub.cancel_at else None,
                    'canceled_at': datetime.fromtimestamp(sub.canceled_at).isoformat() if sub.canceled_at else None,
                    'amount': unit_amount / 100,
                    'amount_display': amount_display,
                    'interval': interval,
                    'stripe_subscription_id': sub.id,
                })

            return Response({
                'subscriptions': result,
                'has_active_subscription': has_active,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class CancelSubscriptionView(APIView):
    """
    Cancel a subscription.

    POST /payments/subscriptions/cancel/
    Body: { subscription_id: string, immediate: boolean } - If immediate is true, cancel immediately. Otherwise cancel at period end.
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        tenant = request.tenant
        subscription_id = request.data.get('subscription_id')
        immediate = request.data.get('immediate', False)
        stripe.api_key = settings.STRIPE_SECRET_KEY

        if not subscription_id:
            return Response(
                {'error': 'subscription_id is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        if not tenant.stripe_customer_id:
            return Response(
                {'error': 'No customer account found'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            # Verify subscription belongs to this customer
            subscription = stripe.Subscription.retrieve(subscription_id)
            if subscription.customer != tenant.stripe_customer_id:
                return Response(
                    {'error': 'Subscription not found'},
                    status=status.HTTP_404_NOT_FOUND
                )

            from datetime import datetime

            if immediate:
                # Cancel immediately
                canceled = stripe.Subscription.cancel(subscription_id)
                message = 'Subscription has been cancelled immediately.'
            else:
                # Cancel at end of billing period
                canceled = stripe.Subscription.modify(
                    subscription_id,
                    cancel_at_period_end=True
                )
                message = 'Subscription will be cancelled at the end of the billing period.'

            # Get period end from subscription items (new Stripe API)
            items_data = canceled.get('items', {}).get('data', [])
            period_end = items_data[0].get('current_period_end') if items_data else canceled.get('billing_cycle_anchor')

            return Response({
                'success': True,
                'message': message,
                'cancel_at_period_end': canceled['cancel_at_period_end'],
                'current_period_end': datetime.fromtimestamp(period_end).isoformat() if period_end else None,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class ReactivateSubscriptionView(APIView):
    """
    Reactivate a subscription that was set to cancel at period end.

    POST /payments/subscriptions/reactivate/
    Body: { subscription_id: string }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        tenant = request.tenant
        subscription_id = request.data.get('subscription_id')
        stripe.api_key = settings.STRIPE_SECRET_KEY

        if not subscription_id:
            return Response(
                {'error': 'subscription_id is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        if not tenant.stripe_customer_id:
            return Response(
                {'error': 'No customer account found'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            # Verify subscription belongs to this customer
            subscription = stripe.Subscription.retrieve(subscription_id)
            if subscription.customer != tenant.stripe_customer_id:
                return Response(
                    {'error': 'Subscription not found'},
                    status=status.HTTP_404_NOT_FOUND
                )

            # Reactivate by removing cancel_at_period_end
            reactivated = stripe.Subscription.modify(
                subscription_id,
                cancel_at_period_end=False
            )

            return Response({
                'success': True,
                'message': 'Subscription has been reactivated.',
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


# ============================================================================
# API Keys Endpoints (Free Tier)
# ============================================================================

class ApiKeysView(TenantRequiredAPIView, APIView):
    """
    Manage Stripe API keys for direct integration (free tier).

    GET /payments/api-keys/
    POST /payments/api-keys/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def get(self, request):
        """Get current API key configuration."""
        tenant = self.tenant

        if not tenant.stripe_secret_key:
            return Response({
                'configured': False,
                'message': 'No API keys configured'
            })

        return Response({
            'configured': True,
            'id': tenant.id,
            'status': tenant.stripe_api_key_status,
            'secret_key_masked': mask_key(tenant.stripe_secret_key),
            'publishable_key_masked': mask_key(tenant.stripe_publishable_key),
            'last_validated_at': tenant.stripe_api_key_validated_at.isoformat() if tenant.stripe_api_key_validated_at else None,
            'stripe_account_id': tenant.stripe_api_key_account_id,
            'stripe_account_name': tenant.stripe_api_key_account_name,
            'validation_error': tenant.stripe_api_key_error,
        })

    def post(self, request):
        """Save and validate API keys."""
        secret_key = request.data.get('secret_key', '').strip()
        publishable_key = request.data.get('publishable_key', '').strip()

        if not secret_key or not publishable_key:
            return self.error_response('Both secret_key and publishable_key are required')

        # Validate keys against Stripe
        validation = validate_stripe_keys(secret_key, publishable_key)

        if not validation['valid']:
            return self.error_response(validation.get('error', 'Invalid API keys'))

        # Save keys to tenant
        tenant = self.tenant
        tenant.stripe_secret_key = secret_key
        tenant.stripe_publishable_key = publishable_key
        tenant.stripe_api_key_status = 'active'
        tenant.stripe_api_key_validated_at = timezone.now()
        tenant.stripe_api_key_account_id = validation.get('account_id', '')
        tenant.stripe_api_key_account_name = validation.get('account_name', '')
        tenant.stripe_api_key_error = ''
        tenant.payment_mode = 'direct_api'
        tenant.save()

        return Response({
            'id': tenant.id,
            'status': 'active',
            'secret_key_masked': mask_key(secret_key),
            'publishable_key_masked': mask_key(publishable_key),
            'last_validated_at': tenant.stripe_api_key_validated_at.isoformat(),
            'stripe_account_id': tenant.stripe_api_key_account_id,
            'stripe_account_name': tenant.stripe_api_key_account_name,
            'validation_error': '',
        }, status=status.HTTP_201_CREATED)


def validate_stripe_keys(secret_key, publishable_key):
    """Validate Stripe API keys. Returns dict with 'valid' key and validation info."""
    try:
        # Test the secret key by retrieving account info
        stripe.api_key = secret_key
        account = stripe.Account.retrieve()

        # Verify publishable key format
        if not publishable_key.startswith('pk_'):
            return {'valid': False, 'error': 'Invalid publishable key format'}

        # Determine environment
        is_test = secret_key.startswith('sk_test_')

        return {
            'valid': True,
            'account_id': account.id,
            'account_name': account.get('business_profile', {}).get('name', '') or account.get('email', ''),
            'environment': 'test' if is_test else 'live',
        }
    except stripe.error.AuthenticationError:
        return {'valid': False, 'error': 'Invalid secret key'}
    except stripe.error.StripeError as e:
        return {'valid': False, 'error': str(e)}
    finally:
        # Reset to platform key
        stripe.api_key = settings.STRIPE_SECRET_KEY


class ApiKeysValidateView(TenantAPIView, APIView):
    """
    Validate API keys without saving.

    POST /payments/api-keys/validate/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Validate keys without saving."""
        secret_key = request.data.get('secret_key', '').strip()
        publishable_key = request.data.get('publishable_key', '').strip()

        if not secret_key or not publishable_key:
            return Response(
                {'valid': False, 'error': 'Both secret_key and publishable_key are required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        validation = validate_stripe_keys(secret_key, publishable_key)
        return Response(validation)


class ApiKeysRevalidateView(APIView):
    """
    Re-validate stored API keys.

    POST /payments/api-keys/revalidate/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Re-validate stored keys."""
        tenant = request.tenant

        if not tenant.stripe_secret_key:
            return Response(
                {'valid': False, 'error': 'No API keys configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            stripe.api_key = tenant.stripe_secret_key
            account = stripe.Account.retrieve()

            # Update tenant with fresh validation
            tenant.stripe_api_key_status = 'active'
            tenant.stripe_api_key_validated_at = timezone.now()
            tenant.stripe_api_key_account_id = account.id
            tenant.stripe_api_key_account_name = account.get('business_profile', {}).get('name', '') or account.get('email', '')
            tenant.stripe_api_key_error = ''
            tenant.save()

            return Response({
                'valid': True,
                'account_id': account.id,
                'account_name': tenant.stripe_api_key_account_name,
            })
        except stripe.error.AuthenticationError:
            tenant.stripe_api_key_status = 'invalid'
            tenant.stripe_api_key_error = 'Invalid secret key'
            tenant.save()
            return Response({'valid': False, 'error': 'Invalid secret key'})
        except stripe.error.StripeError as e:
            tenant.stripe_api_key_status = 'invalid'
            tenant.stripe_api_key_error = str(e)
            tenant.save()
            return Response({'valid': False, 'error': str(e)})
        finally:
            stripe.api_key = settings.STRIPE_SECRET_KEY


class ApiKeysDeleteView(APIView):
    """
    Delete stored API keys.

    DELETE /payments/api-keys/delete/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def delete(self, request):
        """Delete stored keys."""
        tenant = request.tenant

        tenant.stripe_secret_key = ''
        tenant.stripe_publishable_key = ''
        tenant.stripe_api_key_status = ''
        tenant.stripe_api_key_validated_at = None
        tenant.stripe_api_key_account_id = ''
        tenant.stripe_api_key_account_name = ''
        tenant.stripe_api_key_error = ''
        if tenant.payment_mode == 'direct_api':
            tenant.payment_mode = 'none'
        tenant.save()

        return Response({
            'success': True,
            'message': 'API keys deleted successfully'
        })


# ============================================================================
# Stripe Connect Endpoints (Paid Tiers)
# ============================================================================

class ConnectStatusView(APIView):
    """
    Get Connect account status.

    GET /payments/connect/status/
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        """Get current Connect account status."""
        tenant = request.tenant

        if not tenant.stripe_connect_id:
            return Response(
                {'error': 'No Connect account configured'},
                status=status.HTTP_404_NOT_FOUND
            )

        return Response({
            'id': tenant.id,
            'business': tenant.id,
            'business_name': tenant.name,
            'business_subdomain': tenant.schema_name,
            'stripe_account_id': tenant.stripe_connect_id,
            'account_type': 'standard',
            'status': tenant.stripe_connect_status,
            'charges_enabled': tenant.stripe_charges_enabled,
            'payouts_enabled': tenant.stripe_payouts_enabled,
            'details_submitted': tenant.stripe_details_submitted,
            'onboarding_complete': tenant.stripe_onboarding_complete,
            'onboarding_link': None,
            'onboarding_link_expires_at': None,
            'is_onboarding_link_valid': False,
            'created_at': tenant.created_on.isoformat() if tenant.created_on else None,
            'updated_at': tenant.created_on.isoformat() if tenant.created_on else None,
        })


class ConnectOnboardView(APIView):
    """
    Initiate Connect account onboarding.

    POST /payments/connect/onboard/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Start Connect onboarding flow."""
        refresh_url = request.data.get('refresh_url')
        return_url = request.data.get('return_url')

        if not refresh_url or not return_url:
            return Response(
                {'error': 'refresh_url and return_url are required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        tenant = request.tenant
        stripe.api_key = settings.STRIPE_SECRET_KEY

        try:
            # Create or retrieve Connect account
            if not tenant.stripe_connect_id:
                # Create new Standard Connect account
                account = stripe.Account.create(
                    type='standard',
                    email=tenant.contact_email or None,
                    business_profile={
                        'name': tenant.name,
                    },
                    metadata={
                        'tenant_id': str(tenant.id),
                        'tenant_schema': tenant.schema_name,
                    }
                )
                tenant.stripe_connect_id = account.id
                tenant.stripe_connect_status = 'onboarding'
                tenant.payment_mode = 'connect'
                tenant.save()

            # Create account link for onboarding
            account_link = stripe.AccountLink.create(
                account=tenant.stripe_connect_id,
                refresh_url=refresh_url,
                return_url=return_url,
                type='account_onboarding',
            )

            return Response({
                'account_type': 'standard',
                'url': account_link.url,
                'stripe_account_id': tenant.stripe_connect_id,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class ConnectRefreshLinkView(APIView):
    """
    Refresh Connect onboarding link.

    POST /payments/connect/refresh-link/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Get a new onboarding link."""
        refresh_url = request.data.get('refresh_url')
        return_url = request.data.get('return_url')

        if not refresh_url or not return_url:
            return Response(
                {'error': 'refresh_url and return_url are required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        tenant = request.tenant

        if not tenant.stripe_connect_id:
            return Response(
                {'error': 'No Connect account exists'},
                status=status.HTTP_400_BAD_REQUEST
            )

        stripe.api_key = settings.STRIPE_SECRET_KEY

        try:
            account_link = stripe.AccountLink.create(
                account=tenant.stripe_connect_id,
                refresh_url=refresh_url,
                return_url=return_url,
                type='account_onboarding',
            )

            return Response({'url': account_link.url})

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class ConnectAccountSessionView(APIView):
    """
    Create an Account Session for embedded Connect.

    POST /payments/connect/account-session/

    If no Connect account exists, creates a Custom Connect account first.
    Custom accounts are required for embedded onboarding (Standard accounts
    require the redirect flow).
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Create account session for embedded components."""
        tenant = request.tenant
        stripe.api_key = settings.STRIPE_SECRET_KEY

        try:
            # Create Connect account if it doesn't exist
            if not tenant.stripe_connect_id:
                # Create new Custom Connect account (required for embedded onboarding)
                account = stripe.Account.create(
                    type='custom',
                    country='US',
                    email=tenant.contact_email or None,
                    capabilities={
                        'card_payments': {'requested': True},
                        'transfers': {'requested': True},
                    },
                    business_type='company',
                    business_profile={
                        'name': tenant.name,
                        'mcc': '7299',  # Miscellaneous recreation services
                    },
                    metadata={
                        'tenant_id': str(tenant.id),
                        'tenant_schema': tenant.schema_name,
                    }
                )
                tenant.stripe_connect_id = account.id
                tenant.stripe_connect_status = 'onboarding'
                tenant.payment_mode = 'connect'
                tenant.save()

            account_session = stripe.AccountSession.create(
                account=tenant.stripe_connect_id,
                components={
                    'account_onboarding': {'enabled': True},
                    'payments': {'enabled': True},
                    'payouts': {'enabled': True},
                },
            )

            return Response({
                'client_secret': account_session.client_secret,
                'stripe_account_id': tenant.stripe_connect_id,
                'publishable_key': settings.STRIPE_PUBLISHABLE_KEY,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class ConnectRefreshStatusView(APIView):
    """
    Refresh Connect account status from Stripe.

    POST /payments/connect/refresh-status/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Sync local status with Stripe."""
        tenant = request.tenant

        if not tenant.stripe_connect_id:
            return Response(
                {'error': 'No Connect account exists'},
                status=status.HTTP_400_BAD_REQUEST
            )

        stripe.api_key = settings.STRIPE_SECRET_KEY

        try:
            account = stripe.Account.retrieve(tenant.stripe_connect_id)

            # Update tenant with current status
            tenant.stripe_charges_enabled = account.charges_enabled
            tenant.stripe_payouts_enabled = account.payouts_enabled
            tenant.stripe_details_submitted = account.details_submitted

            # Determine status
            if account.charges_enabled and account.payouts_enabled:
                tenant.stripe_connect_status = 'active'
                tenant.stripe_onboarding_complete = True
            elif account.details_submitted:
                tenant.stripe_connect_status = 'onboarding'
            else:
                tenant.stripe_connect_status = 'pending'

            tenant.save()

            return Response({
                'id': tenant.id,
                'business': tenant.id,
                'business_name': tenant.name,
                'business_subdomain': tenant.schema_name,
                'stripe_account_id': tenant.stripe_connect_id,
                'account_type': 'standard',
                'status': tenant.stripe_connect_status,
                'charges_enabled': tenant.stripe_charges_enabled,
                'payouts_enabled': tenant.stripe_payouts_enabled,
                'details_submitted': tenant.stripe_details_submitted,
                'onboarding_complete': tenant.stripe_onboarding_complete,
                'onboarding_link': None,
                'onboarding_link_expires_at': None,
                'is_onboarding_link_valid': False,
                'created_at': tenant.created_on.isoformat() if tenant.created_on else None,
                'updated_at': tenant.created_on.isoformat() if tenant.created_on else None,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


# ============================================================================
# Transaction Endpoints
# ============================================================================

class TransactionListView(APIView):
    """
    List transactions with filtering.

    GET /payments/transactions/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def get(self, request):
        """Get paginated list of transactions."""
        # Get query params
        page = int(request.query_params.get('page', 1))
        page_size = int(request.query_params.get('page_size', 20))
        status_filter = request.query_params.get('status')
        transaction_type = request.query_params.get('transaction_type')
        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')

        # Build queryset
        queryset = TransactionLink.objects.all()

        if status_filter:
            queryset = queryset.filter(status=status_filter.upper())

        if start_date:
            queryset = queryset.filter(created_at__date__gte=start_date)

        if end_date:
            queryset = queryset.filter(created_at__date__lte=end_date)

        # Paginate
        total_count = queryset.count()
        total_pages = (total_count + page_size - 1) // page_size
        offset = (page - 1) * page_size
        transactions = queryset[offset:offset + page_size]

        # Serialize
        results = []
        for tx in transactions:
            results.append({
                'id': tx.id,
                'business': request.tenant.id,
                'business_name': request.tenant.name,
                'stripe_payment_intent_id': tx.payment_intent_id,
                'stripe_charge_id': '',
                'transaction_type': 'payment',
                'status': tx.status.lower(),
                'amount': float(tx.amount),
                'amount_display': f'${tx.amount:.2f}',
                'application_fee_amount': float(tx.application_fee_amount),
                'fee_display': f'${tx.application_fee_amount:.2f}',
                'net_amount': float(tx.tenant_revenue),
                'currency': tx.currency,
                'customer_email': '',
                'customer_name': '',
                'created_at': tx.created_at.isoformat(),
                'updated_at': tx.updated_at.isoformat(),
            })

        return Response({
            'results': results,
            'count': total_count,
            'page': page,
            'page_size': page_size,
            'total_pages': total_pages,
        })


class TransactionSummaryView(APIView):
    """
    Get transaction summary/analytics.

    GET /payments/transactions/summary/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def get(self, request):
        """Get transaction summary."""
        from django.db.models import Sum, Count, Avg

        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')

        queryset = TransactionLink.objects.all()

        if start_date:
            queryset = queryset.filter(created_at__date__gte=start_date)
        if end_date:
            queryset = queryset.filter(created_at__date__lte=end_date)

        # Calculate aggregates
        stats = queryset.aggregate(
            total_transactions=Count('id'),
            total_volume=Sum('amount'),
            total_fees=Sum('application_fee_amount'),
            average_transaction=Avg('amount'),
        )

        successful = queryset.filter(status=TransactionLink.Status.SUCCEEDED).count()
        failed = queryset.filter(status=TransactionLink.Status.FAILED).count()
        refunded = queryset.filter(status=TransactionLink.Status.REFUNDED).count()

        total_volume = stats['total_volume'] or Decimal('0')
        total_fees = stats['total_fees'] or Decimal('0')
        net_revenue = total_volume - total_fees
        avg_tx = stats['average_transaction'] or Decimal('0')

        return Response({
            'total_transactions': stats['total_transactions'] or 0,
            'total_volume': float(total_volume),
            'total_volume_display': f'${total_volume:.2f}',
            'total_fees': float(total_fees),
            'total_fees_display': f'${total_fees:.2f}',
            'net_revenue': float(net_revenue),
            'net_revenue_display': f'${net_revenue:.2f}',
            'successful_transactions': successful,
            'failed_transactions': failed,
            'refunded_transactions': refunded,
            'average_transaction': float(avg_tx),
            'average_transaction_display': f'${avg_tx:.2f}',
        })


class StripeChargesView(APIView):
    """
    Get recent charges from Stripe.

    GET /payments/transactions/charges/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def get(self, request):
        """Get recent charges from Stripe API."""
        limit = int(request.query_params.get('limit', 20))
        tenant = request.tenant

        # Determine which Stripe key to use
        if tenant.payment_mode == 'direct_api' and tenant.stripe_secret_key:
            stripe.api_key = tenant.stripe_secret_key
            stripe_account = None
        elif tenant.payment_mode == 'connect' and tenant.stripe_connect_id:
            stripe.api_key = settings.STRIPE_SECRET_KEY
            stripe_account = tenant.stripe_connect_id
        else:
            return Response({
                'charges': [],
                'has_more': False,
            })

        try:
            params = {'limit': limit}
            if stripe_account:
                params['stripe_account'] = stripe_account

            charges = stripe.Charge.list(**params)

            results = []
            for charge in charges.data:
                results.append({
                    'id': charge.id,
                    'amount': charge.amount,
                    'amount_display': f'${charge.amount / 100:.2f}',
                    'amount_refunded': charge.amount_refunded,
                    'currency': charge.currency,
                    'status': charge.status,
                    'paid': charge.paid,
                    'refunded': charge.refunded,
                    'description': charge.description,
                    'receipt_email': charge.receipt_email,
                    'receipt_url': charge.receipt_url,
                    'created': charge.created,
                    'payment_method_details': dict(charge.payment_method_details) if charge.payment_method_details else None,
                    'billing_details': dict(charge.billing_details) if charge.billing_details else None,
                })

            return Response({
                'charges': results,
                'has_more': charges.has_more,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
        finally:
            stripe.api_key = settings.STRIPE_SECRET_KEY


class StripePayoutsView(APIView):
    """
    Get payouts from Stripe.

    GET /payments/transactions/payouts/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def get(self, request):
        """Get payouts from Stripe API."""
        limit = int(request.query_params.get('limit', 20))
        tenant = request.tenant

        # Determine which Stripe key to use
        if tenant.payment_mode == 'direct_api' and tenant.stripe_secret_key:
            stripe.api_key = tenant.stripe_secret_key
            stripe_account = None
        elif tenant.payment_mode == 'connect' and tenant.stripe_connect_id:
            stripe.api_key = settings.STRIPE_SECRET_KEY
            stripe_account = tenant.stripe_connect_id
        else:
            return Response({
                'payouts': [],
                'has_more': False,
            })

        try:
            params = {'limit': limit}
            if stripe_account:
                params['stripe_account'] = stripe_account

            payouts = stripe.Payout.list(**params)

            results = []
            for payout in payouts.data:
                results.append({
                    'id': payout.id,
                    'amount': payout.amount,
                    'amount_display': f'${payout.amount / 100:.2f}',
                    'currency': payout.currency,
                    'status': payout.status,
                    'arrival_date': payout.arrival_date,
                    'created': payout.created,
                    'description': payout.description,
                    'destination': payout.destination,
                    'failure_message': payout.failure_message,
                    'method': payout.method,
                    'type': payout.type,
                })

            return Response({
                'payouts': results,
                'has_more': payouts.has_more,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
        finally:
            stripe.api_key = settings.STRIPE_SECRET_KEY


class StripeBalanceView(APIView):
    """
    Get current Stripe balance.

    GET /payments/transactions/balance/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def get(self, request):
        """Get balance from Stripe API."""
        tenant = request.tenant

        # Determine which Stripe key to use
        if tenant.payment_mode == 'direct_api' and tenant.stripe_secret_key:
            stripe.api_key = tenant.stripe_secret_key
            stripe_account = None
        elif tenant.payment_mode == 'connect' and tenant.stripe_connect_id:
            stripe.api_key = settings.STRIPE_SECRET_KEY
            stripe_account = tenant.stripe_connect_id
        else:
            return Response({
                'available': [],
                'pending': [],
                'available_total': 0,
                'pending_total': 0,
            })

        try:
            params = {}
            if stripe_account:
                params['stripe_account'] = stripe_account

            balance = stripe.Balance.retrieve(**params)

            available = []
            pending = []
            available_total = 0
            pending_total = 0

            for item in balance.available:
                available.append({
                    'amount': item.amount,
                    'currency': item.currency,
                    'amount_display': f'${item.amount / 100:.2f}',
                })
                if item.currency == 'usd':
                    available_total = item.amount

            for item in balance.pending:
                pending.append({
                    'amount': item.amount,
                    'currency': item.currency,
                    'amount_display': f'${item.amount / 100:.2f}',
                })
                if item.currency == 'usd':
                    pending_total = item.amount

            return Response({
                'available': available,
                'pending': pending,
                'available_total': available_total,
                'pending_total': pending_total,
            })

        except stripe.error.StripeError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
        finally:
            stripe.api_key = settings.STRIPE_SECRET_KEY


class TransactionExportView(APIView):
    """
    Export transaction data.

    POST /payments/transactions/export/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Export transactions to various formats."""
        # This is a placeholder - full implementation would generate CSV/PDF/etc
        format_type = request.data.get('format', 'csv')

        return Response(
            {'error': 'Export functionality not yet implemented'},
            status=status.HTTP_501_NOT_IMPLEMENTED
        )


# ============================================================================
# Payment Intent Endpoints (existing)
# ============================================================================

class CreatePaymentIntentView(APIView):
    """
    Create a Stripe PaymentIntent for an event.

    POST /payments/payment-intents/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Create payment intent for an event"""
        event_id = request.data.get('event_id')
        payment_method_id = request.data.get('payment_method_id')
        locale = request.data.get('locale', 'en')
        amount = request.data.get('amount')

        if not event_id:
            return Response(
                {'error': 'event_id is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            event = Event.objects.get(id=event_id)
        except Event.DoesNotExist:
            return Response(
                {'error': f'Event {event_id} not found'},
                status=status.HTTP_404_NOT_FOUND
            )

        if amount:
            try:
                amount = Decimal(str(amount))
            except (ValueError, TypeError):
                return Response(
                    {'error': 'Invalid amount format'},
                    status=status.HTTP_400_BAD_REQUEST
                )
        else:
            return Response(
                {'error': 'amount is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            stripe_svc = get_stripe_service_for_tenant(request.tenant)
        except ValueError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            payment_intent, transaction = stripe_svc.create_payment_intent(
                event=event,
                amount=amount,
                payment_method_id=payment_method_id,
                locale=locale
            )

            return Response({
                'client_secret': payment_intent.client_secret,
                'payment_intent_id': payment_intent.id,
                'amount': str(transaction.amount),
                'currency': transaction.currency,
                'application_fee_amount': str(transaction.application_fee_amount),
                'status': transaction.status,
            }, status=status.HTTP_201_CREATED)

        except Exception as e:
            return Response(
                {'error': f'Payment intent creation failed: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class TerminalConnectionTokenView(APIView):
    """
    Get a Stripe Terminal connection token.

    POST /payments/terminal/connection-token/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Get terminal connection token"""
        try:
            stripe_svc = get_stripe_service_for_tenant(request.tenant)
        except ValueError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            token = stripe_svc.get_terminal_token()
            return Response({'secret': token.secret})
        except Exception as e:
            return Response(
                {'error': f'Terminal token creation failed: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class RefundPaymentView(APIView):
    """
    Refund a payment.

    POST /payments/refunds/
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request):
        """Create refund"""
        payment_intent_id = request.data.get('payment_intent_id')
        amount = request.data.get('amount')
        reason = request.data.get('reason', 'requested_by_customer')

        if not payment_intent_id:
            return Response(
                {'error': 'payment_intent_id is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        if amount:
            try:
                amount = Decimal(str(amount))
            except (ValueError, TypeError):
                return Response(
                    {'error': 'Invalid amount format'},
                    status=status.HTTP_400_BAD_REQUEST
                )

        try:
            stripe_svc = get_stripe_service_for_tenant(request.tenant)
        except ValueError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            refund = stripe_svc.refund_payment(
                payment_intent_id=payment_intent_id,
                amount=amount,
                reason=reason
            )

            return Response({
                'refund_id': refund.id,
                'amount': refund.amount / 100,
                'status': refund.status,
                'reason': refund.reason,
            }, status=status.HTTP_201_CREATED)

        except Exception as e:
            return Response(
                {'error': f'Refund failed: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


# ============================================================================
# Customer Billing Endpoints
# ============================================================================

class CustomerBillingView(APIView):
    """
    Get customer's billing information including payment history and outstanding payments.

    GET /payments/customer/billing/

    Returns:
    - outstanding: Events with pending payments
    - payment_history: Completed transactions
    - summary: Total spent, etc.
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        """Get customer billing data."""
        from django.contrib.contenttypes.models import ContentType
        from smoothschedule.scheduling.schedule.models import Participant
        from smoothschedule.identity.users.models import User

        user = request.user

        # Verify user is a customer (use uppercase constant from User model)
        if user.role != User.Role.CUSTOMER:
            return Response(
                {'error': 'This endpoint is only for customers'},
                status=status.HTTP_403_FORBIDDEN
            )

        # Get ContentType for User model
        user_content_type = ContentType.objects.get_for_model(User)

        # Find all events where this user is a customer participant
        customer_participations = Participant.objects.filter(
            content_type=user_content_type,
            object_id=user.id,
            role=Participant.Role.CUSTOMER
        ).select_related('event')

        event_ids = [p.event_id for p in customer_participations]

        # Get all transactions for these events
        transactions = TransactionLink.objects.filter(
            event_id__in=event_ids
        ).select_related('event').order_by('-created_at')

        # Split into completed payments and pending
        payment_history = []
        for tx in transactions:
            if tx.status in [TransactionLink.Status.SUCCEEDED, TransactionLink.Status.REFUNDED]:
                # Get service info from event
                service_name = ''
                if tx.event.service:
                    service_name = tx.event.service.name

                payment_history.append({
                    'id': tx.id,
                    'event_id': tx.event_id,
                    'event_title': tx.event.title,
                    'service_name': service_name,
                    'amount': float(tx.amount),
                    'amount_display': f'${tx.amount:.2f}',
                    'currency': tx.currency,
                    'status': tx.status.lower(),
                    'payment_intent_id': tx.payment_intent_id,
                    'created_at': tx.created_at.isoformat(),
                    'completed_at': tx.completed_at.isoformat() if tx.completed_at else None,
                    'event_date': tx.event.start_time.isoformat() if tx.event.start_time else None,
                })

        # Get events with pending/no payment
        events_with_payment = set(tx.event_id for tx in transactions if tx.status == TransactionLink.Status.SUCCEEDED)

        outstanding = []
        for participation in customer_participations:
            event = participation.event
            # Skip if already paid or cancelled
            if event.id in events_with_payment:
                continue
            if event.status == 'CANCELLED':
                continue

            # Get price from service
            price = 0
            service_name = ''
            if event.service:
                price = float(event.service.price)
                service_name = event.service.name

            # Check for pending transaction
            pending_tx = next(
                (tx for tx in transactions if tx.event_id == event.id and tx.status == TransactionLink.Status.PENDING),
                None
            )

            outstanding.append({
                'id': event.id,
                'title': event.title,
                'service_name': service_name,
                'amount': price,
                'amount_display': f'${price:.2f}',
                'status': event.status,
                'start_time': event.start_time.isoformat() if event.start_time else None,
                'end_time': event.end_time.isoformat() if event.end_time else None,
                'payment_status': 'pending' if pending_tx else 'unpaid',
                'payment_intent_id': pending_tx.payment_intent_id if pending_tx else None,
            })

        # Calculate summary
        total_spent = sum(float(tx.amount) for tx in transactions if tx.status == TransactionLink.Status.SUCCEEDED)
        total_outstanding = sum(item['amount'] for item in outstanding)

        return Response({
            'outstanding': outstanding,
            'payment_history': payment_history,
            'summary': {
                'total_spent': total_spent,
                'total_spent_display': f'${total_spent:.2f}',
                'total_outstanding': total_outstanding,
                'total_outstanding_display': f'${total_outstanding:.2f}',
                'payment_count': len(payment_history),
            }
        })


class CustomerPaymentMethodsView(APIView):
    """
    Get/manage customer's saved payment methods.

    GET /payments/customer/payment-methods/

    Note: Payment methods are stored in Stripe, not locally.
    This requires the customer to have a Stripe Customer ID.
    """
    permission_classes = [IsAuthenticated]

    def get(self, request):
        """Get customer's saved payment methods from Stripe."""
        from smoothschedule.identity.users.models import User

        user = request.user

        # Verify user is a customer (use uppercase constant from User model)
        if user.role != User.Role.CUSTOMER:
            return Response(
                {'error': 'This endpoint is only for customers'},
                status=status.HTTP_403_FORBIDDEN
            )

        # Check if user has a Stripe customer ID
        stripe_customer_id = getattr(user, 'stripe_customer_id', None)
        if not stripe_customer_id:
            # No saved payment methods yet
            return Response({
                'payment_methods': [],
                'has_stripe_customer': False,
                'message': 'No saved payment methods. Payment methods will be saved when you make your first payment.'
            })

        try:
            stripe_svc = get_stripe_service_for_tenant(request.tenant)
            payment_methods = stripe_svc.list_payment_methods(stripe_customer_id)

            methods = []
            for pm in payment_methods.data:
                methods.append({
                    'id': pm.id,
                    'type': pm.type,
                    'brand': pm.card.brand if pm.card else None,
                    'last4': pm.card.last4 if pm.card else None,
                    'exp_month': pm.card.exp_month if pm.card else None,
                    'exp_year': pm.card.exp_year if pm.card else None,
                    'is_default': pm.id == getattr(user, 'default_payment_method_id', None),
                })

            return Response({
                'payment_methods': methods,
                'has_stripe_customer': True,
            })

        except ValueError:
            # Stripe not configured for tenant - show friendly message
            return Response({
                'payment_methods': [],
                'has_stripe_customer': False,
                'message': 'Payment methods are not available at this time.'
            })
        except Exception:
            # Don't expose internal errors to customers
            return Response({
                'payment_methods': [],
                'has_stripe_customer': False,
                'message': 'Unable to load payment methods. Please try again later.'
            })


class CustomerSetupIntentView(APIView):
    """
    Create a SetupIntent for adding a new payment method.

    POST /payments/customer/setup-intent/

    Returns client_secret for Stripe Elements to collect card details.
    Supports both direct_api and connect payment modes.
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        """Create a SetupIntent for the customer."""
        import logging
        logger = logging.getLogger(__name__)
        from smoothschedule.identity.users.models import User

        user = request.user
        tenant = request.tenant

        # Verify user is a customer
        if user.role != User.Role.CUSTOMER:
            return Response(
                {'error': 'This endpoint is only for customers'},
                status=status.HTTP_403_FORBIDDEN
            )

        try:
            # Debug logging
            logger.info(f"[CustomerSetupIntentView] Tenant: {tenant.name}")
            logger.info(f"[CustomerSetupIntentView] payment_mode: {tenant.payment_mode}")
            logger.info(f"[CustomerSetupIntentView] stripe_connect_id: '{tenant.stripe_connect_id}'")

            stripe_account = None  # Will be returned to frontend for Stripe.js initialization

            # Determine which Stripe configuration to use
            if tenant.payment_mode == 'direct_api' and tenant.stripe_secret_key:
                # Direct API mode: Use tenant's own API keys
                stripe.api_key = tenant.stripe_secret_key
                stripe_account = None  # No connected account needed

                # Get or create customer ID
                customer_id = getattr(user, 'stripe_customer_id', None)
                if not customer_id:
                    customer = stripe.Customer.create(
                        email=user.email,
                        name=user.get_full_name() or user.username,
                        metadata={
                            'user_id': str(user.id),
                            'tenant': tenant.name,
                        }
                    )
                    customer_id = customer.id
                    user.stripe_customer_id = customer_id
                    user.save(update_fields=['stripe_customer_id'])

                # Create SetupIntent (no stripe_account needed for direct API)
                setup_intent = stripe.SetupIntent.create(
                    customer=customer_id,
                    payment_method_types=['card'],
                )

            elif tenant.payment_mode == 'connect' and tenant.stripe_connect_id:
                # Connect mode: Use platform's API key with connected account
                stripe.api_key = settings.STRIPE_SECRET_KEY
                stripe_account = tenant.stripe_connect_id

                stripe_svc = get_stripe_service_for_tenant(tenant)

                # Create or get Stripe customer on connected account
                customer_id = stripe_svc.create_or_get_customer(user)

                # Create SetupIntent on connected account
                setup_intent = stripe_svc.create_setup_intent(customer_id)

            else:
                # Payment not configured
                logger.warning(f"[CustomerSetupIntentView] Payment not configured for tenant {tenant.name}")
                return Response(
                    {'error': 'Payment methods are not available for this business.'},
                    status=status.HTTP_400_BAD_REQUEST
                )

            logger.info(f"[CustomerSetupIntentView] Created SetupIntent: {setup_intent.id}")
            logger.info(f"[CustomerSetupIntentView] Returning stripe_account: '{stripe_account}'")

            # For direct_api mode, return the tenant's publishable key
            # For connect mode, the frontend uses the platform's publishable key
            publishable_key = None
            if tenant.payment_mode == 'direct_api' and tenant.stripe_publishable_key:
                publishable_key = tenant.stripe_publishable_key

            return Response({
                'client_secret': setup_intent.client_secret,
                'setup_intent_id': setup_intent.id,
                'customer_id': customer_id,
                'stripe_account': stripe_account or '',  # Empty string for direct_api mode
                'publishable_key': publishable_key,  # Only for direct_api mode
            })

        except ValueError as e:
            logger.error(f"[CustomerSetupIntentView] ValueError: {e}")
            return Response(
                {'error': 'Adding payment methods is not available at this time.'},
                status=status.HTTP_400_BAD_REQUEST
            )
        except stripe.error.StripeError as e:
            logger.error(f"[CustomerSetupIntentView] StripeError: {e}")
            return Response(
                {'error': 'Unable to add payment method. Please try again later.'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
        except Exception as e:
            logger.error(f"[CustomerSetupIntentView] Exception: {e}")
            return Response(
                {'error': 'Unable to add payment method. Please try again later.'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class CustomerPaymentMethodDeleteView(APIView):
    """
    Delete a saved payment method.

    DELETE /payments/customer/payment-methods/<payment_method_id>/
    """
    permission_classes = [IsAuthenticated]

    def delete(self, request, payment_method_id):
        """Delete a payment method."""
        from smoothschedule.identity.users.models import User

        user = request.user

        # Verify user is a customer
        if user.role != User.Role.CUSTOMER:
            return Response(
                {'error': 'This endpoint is only for customers'},
                status=status.HTTP_403_FORBIDDEN
            )

        stripe_customer_id = getattr(user, 'stripe_customer_id', None)
        if not stripe_customer_id:
            return Response(
                {'error': 'No payment methods on file'},
                status=status.HTTP_404_NOT_FOUND
            )

        try:
            stripe_svc = get_stripe_service_for_tenant(request.tenant)

            # Verify the payment method belongs to this customer
            payment_methods = stripe_svc.list_payment_methods(stripe_customer_id)
            pm_ids = [pm.id for pm in payment_methods.data]

            if payment_method_id not in pm_ids:
                return Response(
                    {'error': 'Payment method not found'},
                    status=status.HTTP_404_NOT_FOUND
                )

            # Detach the payment method
            stripe_svc.detach_payment_method(payment_method_id)

            return Response({
                'success': True,
                'message': 'Payment method removed successfully'
            })

        except ValueError:
            # Stripe not configured - show friendly message
            return Response(
                {'error': 'Unable to remove payment method at this time.'},
                status=status.HTTP_400_BAD_REQUEST
            )
        except Exception:
            # Don't expose internal errors to customers
            return Response(
                {'error': 'Unable to remove payment method. Please try again later.'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class CustomerPaymentMethodDefaultView(APIView):
    """
    Set a payment method as default.

    POST /payments/customer/payment-methods/<payment_method_id>/default/
    """
    permission_classes = [IsAuthenticated]

    def post(self, request, payment_method_id):
        """Set payment method as default."""
        from smoothschedule.identity.users.models import User

        user = request.user

        # Verify user is a customer
        if user.role != User.Role.CUSTOMER:
            return Response(
                {'error': 'This endpoint is only for customers'},
                status=status.HTTP_403_FORBIDDEN
            )

        stripe_customer_id = getattr(user, 'stripe_customer_id', None)
        if not stripe_customer_id:
            return Response(
                {'error': 'No payment methods on file'},
                status=status.HTTP_404_NOT_FOUND
            )

        try:
            stripe_svc = get_stripe_service_for_tenant(request.tenant)

            # Verify the payment method belongs to this customer
            payment_methods = stripe_svc.list_payment_methods(stripe_customer_id)
            pm_ids = [pm.id for pm in payment_methods.data]

            if payment_method_id not in pm_ids:
                return Response(
                    {'error': 'Payment method not found'},
                    status=status.HTTP_404_NOT_FOUND
                )

            # Set as default
            stripe_svc.set_default_payment_method(stripe_customer_id, payment_method_id)

            # Update user's default payment method
            user.default_payment_method_id = payment_method_id
            user.save(update_fields=['default_payment_method_id'])

            return Response({
                'success': True,
                'message': 'Default payment method updated'
            })

        except ValueError:
            # Stripe not configured - show friendly message
            return Response(
                {'error': 'Unable to update payment method at this time.'},
                status=status.HTTP_400_BAD_REQUEST
            )
        except Exception:
            # Don't expose internal errors to customers
            return Response(
                {'error': 'Unable to update payment method. Please try again later.'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


# ============================================================================
# Variable Pricing / Final Charge Endpoints
# ============================================================================

class SetFinalPriceView(APIView):
    """
    Set the final price for a variable-pricing event and charge the customer.

    POST /payments/events/<event_id>/final-price/

    Body: {
        final_price: number,  # The final price determined after service
        charge_now: boolean   # If true, immediately charge the customer's saved payment method
    }

    This endpoint:
    1. Sets the final_price on the event
    2. If charge_now is true, charges the customer's saved payment method
       for the remaining balance (final_price - deposit_amount)
    3. Updates event status to PAID if successful
    """
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_accept_payments')]

    def post(self, request, event_id):
        """Set final price and optionally charge customer."""
        import logging
        logger = logging.getLogger(__name__)
        from django.contrib.contenttypes.models import ContentType
        from smoothschedule.scheduling.schedule.models import Participant
        from smoothschedule.identity.users.models import User

        final_price = request.data.get('final_price')
        charge_now = request.data.get('charge_now', True)

        if final_price is None:
            return Response(
                {'error': 'final_price is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            final_price = Decimal(str(final_price))
            if final_price < 0:
                raise ValueError("Price cannot be negative")
        except (ValueError, TypeError) as e:
            return Response(
                {'error': f'Invalid final_price: {str(e)}'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Get the event
        try:
            event = Event.objects.get(id=event_id)
        except Event.DoesNotExist:
            return Response(
                {'error': 'Event not found'},
                status=status.HTTP_404_NOT_FOUND
            )

        # Verify event uses variable pricing
        if not event.is_variable_pricing:
            return Response(
                {'error': 'This event does not use variable pricing'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Set the final price
        event.final_price = final_price
        event.status = Event.Status.AWAITING_PAYMENT
        event.save()

        # Calculate remaining balance
        deposit = event.deposit_amount or Decimal('0.00')
        remaining_balance = max(final_price - deposit, Decimal('0.00'))
        overpaid = max(deposit - final_price, Decimal('0.00'))

        response_data = {
            'event_id': event.id,
            'final_price': str(final_price),
            'deposit_amount': str(deposit),
            'remaining_balance': str(remaining_balance),
            'overpaid_amount': str(overpaid) if overpaid > 0 else None,
        }

        if not charge_now:
            return Response(response_data)

        # Charge the remaining balance if there is one
        if remaining_balance <= 0:
            # Final price is less than or equal to deposit - nothing to charge
            event.status = Event.Status.PAID
            event.save()
            response_data['status'] = 'paid'
            response_data['message'] = 'Deposit covered the final price. No additional charge needed.'
            if overpaid > 0:
                response_data['message'] += f' Customer overpaid by ${overpaid}. Consider issuing a refund.'
            return Response(response_data)

        # Find the customer for this event
        user_content_type = ContentType.objects.get_for_model(User)
        customer_participant = Participant.objects.filter(
            event=event,
            content_type=user_content_type,
            role=Participant.Role.CUSTOMER
        ).first()

        if not customer_participant:
            return Response(
                {'error': 'No customer found for this event'},
                status=status.HTTP_400_BAD_REQUEST
            )

        customer = customer_participant.content_object

        # Get customer's default payment method
        stripe_customer_id = getattr(customer, 'stripe_customer_id', None)
        default_payment_method = getattr(customer, 'default_payment_method_id', None)

        if not stripe_customer_id or not default_payment_method:
            return Response(
                {'error': 'Customer does not have a saved payment method'},
                status=status.HTTP_400_BAD_REQUEST
            )

        tenant = request.tenant

        try:
            # Determine Stripe configuration
            if tenant.payment_mode == 'direct_api' and tenant.stripe_secret_key:
                stripe.api_key = tenant.stripe_secret_key
                stripe_account_param = {}
            elif tenant.payment_mode == 'connect' and tenant.stripe_connect_id:
                stripe.api_key = settings.STRIPE_SECRET_KEY
                stripe_account_param = {'stripe_account': tenant.stripe_connect_id}
            else:
                return Response(
                    {'error': 'Payment processing is not configured'},
                    status=status.HTTP_400_BAD_REQUEST
                )

            # Calculate platform fee (5%)
            application_fee_amount = (remaining_balance * Decimal('5.0') / 100).quantize(Decimal('0.01'))
            amount_cents = int(remaining_balance * 100)
            fee_cents = int(application_fee_amount * 100)

            # Create PaymentIntent and charge immediately
            pi_params = {
                'amount': amount_cents,
                'currency': tenant.currency.lower() if hasattr(tenant, 'currency') and tenant.currency else 'usd',
                'customer': stripe_customer_id,
                'payment_method': default_payment_method,
                'off_session': True,  # Customer not present
                'confirm': True,  # Charge immediately
                'metadata': {
                    'event_id': event.id,
                    'event_title': event.title,
                    'charge_type': 'final_balance',
                    'final_price': str(final_price),
                    'deposit_amount': str(deposit),
                },
            }

            # Add application fee for Connect mode
            if tenant.payment_mode == 'connect':
                pi_params['application_fee_amount'] = fee_cents
                pi_params.update(stripe_account_param)

            payment_intent = stripe.PaymentIntent.create(**pi_params)

            # Save transaction
            transaction = TransactionLink.objects.create(
                event=event,
                payment_intent_id=payment_intent.id,
                amount=remaining_balance,
                application_fee_amount=application_fee_amount if tenant.payment_mode == 'connect' else Decimal('0'),
                currency=payment_intent.currency.upper(),
                status=TransactionLink.Status.SUCCEEDED if payment_intent.status == 'succeeded' else TransactionLink.Status.PENDING,
            )

            # Update event
            event.final_charge_transaction_id = payment_intent.id
            if payment_intent.status == 'succeeded':
                event.status = Event.Status.PAID
            event.save()

            logger.info(f"[SetFinalPriceView] Charged ${remaining_balance} for event {event.id}")

            response_data['status'] = 'paid' if payment_intent.status == 'succeeded' else 'pending'
            response_data['payment_intent_id'] = payment_intent.id
            response_data['transaction_id'] = transaction.id
            response_data['message'] = f'Successfully charged ${remaining_balance} to customer\'s saved payment method.'

            return Response(response_data)

        except stripe.error.CardError as e:
            logger.error(f"[SetFinalPriceView] CardError: {e}")
            return Response(
                {'error': f'Card declined: {e.user_message}'},
                status=status.HTTP_400_BAD_REQUEST
            )
        except stripe.error.StripeError as e:
            logger.error(f"[SetFinalPriceView] StripeError: {e}")
            return Response(
                {'error': safe_error_message(e, 'Payment failed. Please try again.')},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
        finally:
            # Reset Stripe key
            stripe.api_key = settings.STRIPE_SECRET_KEY


class EventPricingInfoView(APIView):
    """
    Get pricing information for an event.

    GET /payments/events/<event_id>/pricing/

    Returns deposit, final price, remaining balance, payment status, etc.
    """
    permission_classes = [IsAuthenticated]

    def get(self, request, event_id):
        """Get event pricing info."""
        try:
            event = Event.objects.select_related('service').get(id=event_id)
        except Event.DoesNotExist:
            return Response(
                {'error': 'Event not found'},
                status=status.HTTP_404_NOT_FOUND
            )

        # Get associated transactions
        transactions = TransactionLink.objects.filter(event=event).order_by('-created_at')

        deposit_transaction = None
        final_transaction = None
        for tx in transactions:
            if not deposit_transaction and event.deposit_transaction_id and tx.payment_intent_id == event.deposit_transaction_id:
                deposit_transaction = tx
            if not final_transaction and event.final_charge_transaction_id and tx.payment_intent_id == event.final_charge_transaction_id:
                final_transaction = tx

        response = {
            'event_id': event.id,
            'service_id': event.service_id,
            'service_name': event.service.name if event.service else None,
            'is_variable_pricing': event.is_variable_pricing,
            'status': event.status,
            # Pricing
            'service_price': str(event.service.price) if event.service else None,
            'deposit_amount': str(event.deposit_amount) if event.deposit_amount else None,
            'final_price': str(event.final_price) if event.final_price else None,
            'remaining_balance': str(event.remaining_balance) if event.remaining_balance is not None else None,
            'overpaid_amount': str(event.overpaid_amount) if event.overpaid_amount else None,
            # Transactions
            'deposit_transaction': {
                'id': deposit_transaction.id,
                'status': deposit_transaction.status,
                'amount': str(deposit_transaction.amount),
                'created_at': deposit_transaction.created_at.isoformat(),
            } if deposit_transaction else None,
            'final_transaction': {
                'id': final_transaction.id,
                'status': final_transaction.status,
                'amount': str(final_transaction.amount),
                'created_at': final_transaction.created_at.isoformat(),
            } if final_transaction else None,
        }

        return Response(response)
