"""
Platform Views
API views for platform-level operations
"""
import secrets
from datetime import timedelta

from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from django.db.models import Count
from django.db import transaction, connection
from django.utils import timezone
from django_tenants.utils import schema_context

from smoothschedule.identity.core.models import Tenant, Domain
from smoothschedule.identity.users.models import User
from smoothschedule.billing.models import TenantCustomTier
from smoothschedule.billing.api.serializers import TenantCustomTierSerializer
from .models import TenantInvitation, PlatformSettings, SubscriptionPlan, PlatformEmailAddress
from .serializers import (
    TenantSerializer,
    TenantCreateSerializer,
    TenantUpdateSerializer,
    PlatformUserSerializer,
    PlatformMetricsSerializer,
    TenantInvitationSerializer,
    TenantInvitationCreateSerializer,
    TenantInvitationAcceptSerializer,
    TenantInvitationDetailSerializer,
    PlatformSettingsSerializer,
    StripeKeysUpdateSerializer,
    OAuthSettingsSerializer,
    OAuthSettingsResponseSerializer,
    SubscriptionPlanSerializer,
    SubscriptionPlanCreateSerializer,
    PlatformEmailAddressListSerializer,
    PlatformEmailAddressSerializer,
    PlatformEmailAddressCreateSerializer,
    PlatformEmailAddressUpdateSerializer,
)
from .permissions import IsPlatformAdmin, IsPlatformUser


class PlatformSettingsView(APIView):
    """
    GET /api/platform/settings/
    Get platform settings (Stripe config status, etc.)
    """
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    def get(self, request):
        settings = PlatformSettings.get_instance()
        serializer = PlatformSettingsSerializer(settings)
        return Response(serializer.data)


class StripeKeysView(APIView):
    """
    POST /api/platform/settings/stripe/keys/
    Update Stripe API keys
    """
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    def post(self, request):
        serializer = StripeKeysUpdateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        settings = PlatformSettings.get_instance()

        # Update keys if provided
        if serializer.validated_data.get('stripe_secret_key'):
            settings.stripe_secret_key = serializer.validated_data['stripe_secret_key']
        if serializer.validated_data.get('stripe_publishable_key'):
            settings.stripe_publishable_key = serializer.validated_data['stripe_publishable_key']
        if serializer.validated_data.get('stripe_webhook_secret'):
            settings.stripe_webhook_secret = serializer.validated_data['stripe_webhook_secret']

        # Clear validation status when keys change
        settings.stripe_keys_validated_at = None
        settings.stripe_validation_error = ''
        settings.stripe_account_id = ''
        settings.stripe_account_name = ''
        settings.save()

        return Response(PlatformSettingsSerializer(settings).data)


class StripeValidateView(APIView):
    """
    POST /api/platform/settings/stripe/validate/
    Validate Stripe API keys by making a test API call
    """
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    def post(self, request):
        settings = PlatformSettings.get_instance()

        if not settings.has_stripe_keys():
            return Response(
                {'error': 'No Stripe keys configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            import stripe
            stripe.api_key = settings.get_stripe_secret_key()

            # Try to retrieve account info
            account = stripe.Account.retrieve()

            # Update settings with account info
            settings.stripe_account_id = account.id
            settings.stripe_account_name = account.get('business_profile', {}).get('name', '') or account.get('email', '')
            settings.stripe_keys_validated_at = timezone.now()
            settings.stripe_validation_error = ''
            settings.save()

            return Response({
                'valid': True,
                'account_id': settings.stripe_account_id,
                'account_name': settings.stripe_account_name,
                'settings': PlatformSettingsSerializer(settings).data
            })

        except stripe.error.AuthenticationError as e:
            settings.stripe_validation_error = str(e)
            settings.stripe_keys_validated_at = None
            settings.save()
            return Response({
                'valid': False,
                'error': 'Invalid API key',
                'settings': PlatformSettingsSerializer(settings).data
            }, status=status.HTTP_400_BAD_REQUEST)

        except Exception as e:
            settings.stripe_validation_error = str(e)
            settings.save()
            return Response({
                'valid': False,
                'error': str(e),
                'settings': PlatformSettingsSerializer(settings).data
            }, status=status.HTTP_400_BAD_REQUEST)


class GeneralSettingsView(APIView):
    """
    POST /api/platform/settings/general/
    Update general platform settings (email check interval, etc.)
    """
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    def post(self, request):
        settings = PlatformSettings.get_instance()

        # Update email check interval if provided
        email_check_interval = request.data.get('email_check_interval_minutes')
        if email_check_interval is not None:
            try:
                interval = int(email_check_interval)
                if interval < 1:
                    return Response(
                        {'error': 'Email check interval must be at least 1 minute'},
                        status=status.HTTP_400_BAD_REQUEST
                    )
                if interval > 60:
                    return Response(
                        {'error': 'Email check interval cannot exceed 60 minutes'},
                        status=status.HTTP_400_BAD_REQUEST
                    )
                settings.email_check_interval_minutes = interval
            except (ValueError, TypeError):
                return Response(
                    {'error': 'Invalid email check interval'},
                    status=status.HTTP_400_BAD_REQUEST
                )

        settings.save()
        return Response(PlatformSettingsSerializer(settings).data)


class OAuthSettingsView(APIView):
    """
    GET/POST /api/platform/settings/oauth/
    Get or update OAuth provider settings
    """
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    PROVIDERS = ['google', 'apple', 'facebook', 'linkedin', 'microsoft', 'twitter', 'twitch']

    def _mask_secret(self, secret):
        """Mask a secret string"""
        if not secret:
            return ''
        if len(secret) <= 8:
            return '*' * len(secret)
        return f"{secret[:4]}...{secret[-4:]}"

    def _format_provider_settings(self, provider, oauth_settings):
        """Format provider settings for response"""
        prefix = f"{provider}"
        settings_dict = oauth_settings.get(provider, {})

        result = {
            'enabled': settings_dict.get('enabled', False),
            'client_id': settings_dict.get('client_id', ''),
            'client_secret': self._mask_secret(settings_dict.get('client_secret', '')),
        }

        # Add provider-specific fields
        if provider == 'apple':
            result['team_id'] = settings_dict.get('team_id', '')
            result['key_id'] = settings_dict.get('key_id', '')
        elif provider == 'microsoft':
            result['tenant_id'] = settings_dict.get('tenant_id', '')

        return result

    def get(self, request):
        settings = PlatformSettings.get_instance()
        oauth_settings = settings.oauth_settings or {}

        response_data = {
            'oauth_allow_registration': oauth_settings.get('allow_registration', True),
        }

        for provider in self.PROVIDERS:
            response_data[provider] = self._format_provider_settings(provider, oauth_settings)

        return Response(response_data)

    def post(self, request):
        serializer = OAuthSettingsSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        settings = PlatformSettings.get_instance()
        oauth_settings = settings.oauth_settings or {}

        # Update allow_registration
        if 'oauth_allow_registration' in serializer.validated_data:
            oauth_settings['allow_registration'] = serializer.validated_data['oauth_allow_registration']

        # Update each provider's settings
        for provider in self.PROVIDERS:
            if provider not in oauth_settings:
                oauth_settings[provider] = {}

            enabled_key = f'oauth_{provider}_enabled'
            client_id_key = f'oauth_{provider}_client_id'
            client_secret_key = f'oauth_{provider}_client_secret'

            if enabled_key in serializer.validated_data:
                oauth_settings[provider]['enabled'] = serializer.validated_data[enabled_key]
            if client_id_key in serializer.validated_data:
                oauth_settings[provider]['client_id'] = serializer.validated_data[client_id_key]
            if client_secret_key in serializer.validated_data:
                # Only update if not empty (don't overwrite with empty string)
                if serializer.validated_data[client_secret_key]:
                    oauth_settings[provider]['client_secret'] = serializer.validated_data[client_secret_key]

            # Provider-specific fields
            if provider == 'apple':
                if f'oauth_apple_team_id' in serializer.validated_data:
                    oauth_settings[provider]['team_id'] = serializer.validated_data['oauth_apple_team_id']
                if f'oauth_apple_key_id' in serializer.validated_data:
                    oauth_settings[provider]['key_id'] = serializer.validated_data['oauth_apple_key_id']
            elif provider == 'microsoft':
                if f'oauth_microsoft_tenant_id' in serializer.validated_data:
                    oauth_settings[provider]['tenant_id'] = serializer.validated_data['oauth_microsoft_tenant_id']

        settings.oauth_settings = oauth_settings
        settings.save()

        # Return updated settings
        response_data = {
            'oauth_allow_registration': oauth_settings.get('allow_registration', True),
        }
        for provider in self.PROVIDERS:
            response_data[provider] = self._format_provider_settings(provider, oauth_settings)

        return Response(response_data)


class StripeWebhooksView(APIView):
    """
    GET /api/platform/settings/stripe/webhooks/
    List all Stripe webhook endpoints

    POST /api/platform/settings/stripe/webhooks/
    Create a new webhook endpoint
    """
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    # Default events to subscribe to
    DEFAULT_EVENTS = [
        "checkout.session.completed",
        "checkout.session.expired",
        "customer.created",
        "customer.updated",
        "customer.deleted",
        "customer.subscription.created",
        "customer.subscription.updated",
        "customer.subscription.deleted",
        "customer.subscription.trial_will_end",
        "invoice.created",
        "invoice.finalized",
        "invoice.paid",
        "invoice.payment_failed",
        "invoice.payment_action_required",
        "payment_intent.succeeded",
        "payment_intent.payment_failed",
        "payment_intent.canceled",
        "payment_method.attached",
        "payment_method.detached",
        "account.updated",  # For Connect
        "account.application.authorized",
        "account.application.deauthorized",
    ]

    def _format_webhook(self, endpoint):
        """Format webhook endpoint for response"""
        return {
            'id': endpoint.id,
            'url': endpoint.url,
            'status': endpoint.status,
            'enabled_events': endpoint.enabled_events,
            'api_version': endpoint.api_version,
            'created': endpoint.created.isoformat() if endpoint.created else None,
            'livemode': endpoint.livemode,
            # Don't expose the secret, just indicate if it exists
            'has_secret': bool(endpoint.secret),
        }

    def get(self, request):
        """List all webhook endpoints from Stripe"""
        import stripe
        from djstripe.models import WebhookEndpoint

        settings = PlatformSettings.get_instance()
        if not settings.has_stripe_keys():
            return Response(
                {'error': 'Stripe keys not configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            stripe.api_key = settings.get_stripe_secret_key()

            # Fetch from Stripe API
            stripe_webhooks = stripe.WebhookEndpoint.list(limit=100)

            # Sync to local database and format response
            webhooks = []
            for wh in stripe_webhooks.data:
                # Sync to dj-stripe
                local_wh = WebhookEndpoint.sync_from_stripe_data(wh)
                webhooks.append(self._format_webhook(local_wh))

            return Response({
                'webhooks': webhooks,
                'count': len(webhooks),
            })

        except stripe.error.AuthenticationError:
            return Response(
                {'error': 'Invalid Stripe API key'},
                status=status.HTTP_400_BAD_REQUEST
            )
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    def post(self, request):
        """Create a new webhook endpoint"""
        import stripe
        from djstripe.models import WebhookEndpoint

        settings = PlatformSettings.get_instance()
        if not settings.has_stripe_keys():
            return Response(
                {'error': 'Stripe keys not configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        url = request.data.get('url')
        if not url:
            return Response(
                {'error': 'URL is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Validate URL format
        if not url.startswith('https://'):
            return Response(
                {'error': 'Webhook URL must use HTTPS'},
                status=status.HTTP_400_BAD_REQUEST
            )

        enabled_events = request.data.get('enabled_events', self.DEFAULT_EVENTS)
        description = request.data.get('description', 'SmoothSchedule Platform Webhook')

        try:
            stripe.api_key = settings.get_stripe_secret_key()

            # Create webhook on Stripe
            endpoint = stripe.WebhookEndpoint.create(
                url=url,
                enabled_events=enabled_events,
                description=description,
                metadata={'created_by': 'smoothschedule_platform'},
            )

            # The secret is only returned on creation - save it
            webhook_secret = endpoint.secret

            # Sync to local database
            local_wh = WebhookEndpoint.sync_from_stripe_data(endpoint)

            # Store the secret in local DB (it's not returned by Stripe after creation)
            local_wh.secret = webhook_secret
            local_wh.save()

            # Also update platform settings if this is the primary webhook
            if request.data.get('set_as_primary', False):
                settings.stripe_webhook_secret = webhook_secret
                settings.save()

            return Response({
                'webhook': self._format_webhook(local_wh),
                'secret': webhook_secret,  # Only returned on creation!
                'message': 'Webhook created successfully. Save the secret - it will not be shown again.',
            }, status=status.HTTP_201_CREATED)

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


class StripeWebhookDetailView(APIView):
    """
    GET /api/platform/settings/stripe/webhooks/<id>/
    Get a specific webhook endpoint

    PATCH /api/platform/settings/stripe/webhooks/<id>/
    Update a webhook endpoint

    DELETE /api/platform/settings/stripe/webhooks/<id>/
    Delete a webhook endpoint
    """
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    def _format_webhook(self, endpoint):
        """Format webhook endpoint for response"""
        return {
            'id': endpoint.id,
            'url': endpoint.url,
            'status': endpoint.status,
            'enabled_events': endpoint.enabled_events,
            'api_version': endpoint.api_version,
            'created': endpoint.created.isoformat() if endpoint.created else None,
            'livemode': endpoint.livemode,
            'has_secret': bool(endpoint.secret),
        }

    def get(self, request, webhook_id):
        """Get a specific webhook endpoint"""
        import stripe
        from djstripe.models import WebhookEndpoint

        settings = PlatformSettings.get_instance()
        if not settings.has_stripe_keys():
            return Response(
                {'error': 'Stripe keys not configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            stripe.api_key = settings.get_stripe_secret_key()
            endpoint = stripe.WebhookEndpoint.retrieve(webhook_id)
            local_wh = WebhookEndpoint.sync_from_stripe_data(endpoint)

            return Response({'webhook': self._format_webhook(local_wh)})

        except stripe.error.InvalidRequestError as e:
            return Response(
                {'error': 'Webhook endpoint not found'},
                status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    def patch(self, request, webhook_id):
        """Update a webhook endpoint"""
        import stripe
        from djstripe.models import WebhookEndpoint

        settings = PlatformSettings.get_instance()
        if not settings.has_stripe_keys():
            return Response(
                {'error': 'Stripe keys not configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            stripe.api_key = settings.get_stripe_secret_key()

            # Build update params
            update_params = {}

            if 'url' in request.data:
                url = request.data['url']
                if not url.startswith('https://'):
                    return Response(
                        {'error': 'Webhook URL must use HTTPS'},
                        status=status.HTTP_400_BAD_REQUEST
                    )
                update_params['url'] = url

            if 'enabled_events' in request.data:
                update_params['enabled_events'] = request.data['enabled_events']

            if 'disabled' in request.data:
                update_params['disabled'] = request.data['disabled']

            if 'description' in request.data:
                update_params['description'] = request.data['description']

            if not update_params:
                return Response(
                    {'error': 'No valid fields to update'},
                    status=status.HTTP_400_BAD_REQUEST
                )

            # Update on Stripe
            endpoint = stripe.WebhookEndpoint.modify(webhook_id, **update_params)

            # Sync to local database
            local_wh = WebhookEndpoint.sync_from_stripe_data(endpoint)

            return Response({
                'webhook': self._format_webhook(local_wh),
                'message': 'Webhook updated successfully',
            })

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

    def delete(self, request, webhook_id):
        """Delete a webhook endpoint"""
        import stripe
        from djstripe.models import WebhookEndpoint

        settings = PlatformSettings.get_instance()
        if not settings.has_stripe_keys():
            return Response(
                {'error': 'Stripe keys not configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            stripe.api_key = settings.get_stripe_secret_key()

            # Delete on Stripe
            stripe.WebhookEndpoint.delete(webhook_id)

            # Delete from local database
            WebhookEndpoint.objects.filter(id=webhook_id).delete()

            return Response({
                'message': 'Webhook deleted successfully',
            }, status=status.HTTP_200_OK)

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


class StripeWebhookRotateSecretView(APIView):
    """
    POST /api/platform/settings/stripe/webhooks/<id>/rotate-secret/
    Rotate the webhook signing secret
    """
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    def post(self, request, webhook_id):
        """Rotate the webhook signing secret"""
        import stripe
        from djstripe.models import WebhookEndpoint

        settings = PlatformSettings.get_instance()
        if not settings.has_stripe_keys():
            return Response(
                {'error': 'Stripe keys not configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            stripe.api_key = settings.get_stripe_secret_key()

            # Rotate the secret - this creates a new secret while keeping the old one valid briefly
            # Note: Stripe API doesn't have a direct "rotate" - we need to delete and recreate
            # or use the webhook endpoint's secret rotation if available

            # Get current endpoint
            current = stripe.WebhookEndpoint.retrieve(webhook_id)

            # Delete and recreate with same settings
            stripe.WebhookEndpoint.delete(webhook_id)

            new_endpoint = stripe.WebhookEndpoint.create(
                url=current.url,
                enabled_events=current.enabled_events,
                description=current.get('description', ''),
                metadata=current.get('metadata', {}),
            )

            new_secret = new_endpoint.secret

            # Sync to local database
            WebhookEndpoint.objects.filter(id=webhook_id).delete()
            local_wh = WebhookEndpoint.sync_from_stripe_data(new_endpoint)
            local_wh.secret = new_secret
            local_wh.save()

            # Update platform settings if this was the primary webhook
            if request.data.get('update_platform_secret', False):
                settings.stripe_webhook_secret = new_secret
                settings.save()

            return Response({
                'webhook_id': new_endpoint.id,
                'secret': new_secret,
                'message': 'Webhook secret rotated. Save the new secret - it will not be shown again.',
                'note': 'The webhook ID has changed due to recreation.',
            })

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


class SubscriptionPlanViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing subscription plans.
    Platform admins only.
    """
    queryset = SubscriptionPlan.objects.all().order_by('price_monthly', 'name')
    serializer_class = SubscriptionPlanSerializer
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    def get_serializer_class(self):
        if self.action == 'create':
            return SubscriptionPlanCreateSerializer
        return SubscriptionPlanSerializer

    @action(detail=True, methods=['post'])
    def sync_tenants(self, request, pk=None):
        """
        Sync this plan's permissions to all tenants on this plan.
        This is called explicitly by the admin after confirming they want to sync.
        """
        plan = self.get_object()

        from .tasks import sync_subscription_plan_to_tenants
        import logging
        logger = logging.getLogger(__name__)
        logger.info(f"SubscriptionPlan '{plan.name}' (ID: {plan.id}) - "
                    f"sync to tenants requested by platform admin")

        # Run the sync task
        sync_subscription_plan_to_tenants.delay(plan.id)

        # Count tenants on this plan
        from smoothschedule.identity.core.models import Tenant
        tenant_count = Tenant.objects.filter(subscription_plan=plan).count()

        return Response({
            'message': f'Syncing permissions to {tenant_count} tenant(s) on the "{plan.name}" plan',
            'tenant_count': tenant_count
        })

    @action(detail=False, methods=['post'])
    def sync_with_stripe(self, request):
        """
        Sync subscription plans with Stripe products.
        Creates Stripe products/prices for plans that don't have them.
        """
        import stripe
        from django.conf import settings

        stripe.api_key = settings.STRIPE_SECRET_KEY

        if not stripe.api_key:
            return Response(
                {'error': 'Stripe API key not configured'},
                status=status.HTTP_400_BAD_REQUEST
            )

        synced = []
        errors = []

        for plan in SubscriptionPlan.objects.filter(is_active=True):
            # Skip if already has Stripe IDs
            if plan.stripe_product_id and plan.stripe_price_id:
                synced.append({
                    'id': plan.id,
                    'name': plan.name,
                    'status': 'already_synced'
                })
                continue

            try:
                # Create or retrieve product
                if not plan.stripe_product_id:
                    product = stripe.Product.create(
                        name=plan.name,
                        description=plan.description or f"{plan.name} subscription plan",
                        metadata={
                            'plan_id': str(plan.id),
                            'plan_type': plan.plan_type,
                        }
                    )
                    plan.stripe_product_id = product.id
                else:
                    product = stripe.Product.retrieve(plan.stripe_product_id)

                # Create price if we have monthly pricing
                if not plan.stripe_price_id and plan.price_monthly:
                    price = stripe.Price.create(
                        product=product.id,
                        unit_amount=int(plan.price_monthly * 100),
                        currency='usd',
                        recurring={'interval': 'month'},
                        metadata={'plan_id': str(plan.id)}
                    )
                    plan.stripe_price_id = price.id

                plan.save()
                synced.append({
                    'id': plan.id,
                    'name': plan.name,
                    'status': 'synced',
                    'stripe_product_id': plan.stripe_product_id,
                    'stripe_price_id': plan.stripe_price_id
                })

            except stripe.error.StripeError as e:
                errors.append({
                    'id': plan.id,
                    'name': plan.name,
                    'error': str(e)
                })

        return Response({
            'synced': synced,
            'errors': errors,
            'message': f'Synced {len(synced)} plans, {len(errors)} errors'
        })


class TenantViewSet(viewsets.ModelViewSet):
    """
    ViewSet for viewing, creating, updating, and deleting tenants (businesses).
    Platform admins only.
    """
    queryset = Tenant.objects.all().order_by('-created_on')
    serializer_class = TenantSerializer
    permission_classes = [IsAuthenticated, IsPlatformAdmin]
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options']

    def get_queryset(self):
        """Filter out public schema and optionally filter by active status"""
        queryset = super().get_queryset()
        # Exclude the public schema (it's the platform, not a business)
        queryset = queryset.exclude(schema_name='public')
        is_active = self.request.query_params.get('is_active')
        if is_active is not None:
            queryset = queryset.filter(is_active=is_active.lower() == 'true')
        return queryset

    def get_serializer_class(self):
        """Use different serializer for different actions"""
        if self.action == 'create':
            return TenantCreateSerializer
        if self.action in ['partial_update', 'update']:
            return TenantUpdateSerializer
        return TenantSerializer

    def destroy(self, request, *args, **kwargs):
        """
        Delete a tenant and all its data.
        This will drop the tenant's schema and remove all associated domains and users.
        Only superusers can perform this action.
        """
        tenant = self.get_object()

        # Only superusers can delete tenants
        if request.user.role != User.Role.SUPERUSER:
            return Response(
                {"detail": "Only superusers can delete tenants."},
                status=status.HTTP_403_FORBIDDEN
            )

        # First, unlink staff_resources from users WITHIN the tenant's schema
        # This prevents cross-schema SET_NULL cascade issues when users are deleted
        with schema_context(tenant.schema_name):
            from smoothschedule.scheduling.schedule.models import Resource
            # Unlink all resources from users (set user_id to NULL)
            Resource.objects.filter(user__isnull=False).update(user=None)

        # Delete all users associated with this tenant
        # Use _raw_delete to avoid triggering cascades
        # (cascades would try to access tenant schema tables which may not exist from public)
        user_ids = list(User.objects.filter(tenant=tenant).values_list('id', flat=True))
        if user_ids:
            # Delete related objects that are in the public schema first
            from rest_framework.authtoken.models import Token
            Token.objects.filter(user_id__in=user_ids).delete()

            # Delete MFA-related objects
            from smoothschedule.identity.users.models import EmailVerificationToken, MFAVerificationCode, TrustedDevice
            EmailVerificationToken.objects.filter(user_id__in=user_ids).delete()
            MFAVerificationCode.objects.filter(user_id__in=user_ids).delete()
            TrustedDevice.objects.filter(user_id__in=user_ids).delete()

            # Now delete users using raw SQL to skip Django's cascade
            from django.db import connection
            with connection.cursor() as cursor:
                cursor.execute(
                    "DELETE FROM users_user WHERE id = ANY(%s)",
                    [user_ids]
                )

        # Delete the tenant (this will drop the schema due to django-tenants)
        tenant.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)

    @action(detail=False, methods=['get'])
    def metrics(self, request):
        """Get platform-wide tenant metrics"""
        # Exclude public schema from counts (it's the platform, not a business)
        total_tenants = Tenant.objects.exclude(schema_name='public').count()
        active_tenants = Tenant.objects.exclude(schema_name='public').filter(is_active=True).count()

        metrics = {
            'total_tenants': total_tenants,
            'active_tenants': active_tenants,
            'total_users': User.objects.count(),
            'mrr': 0,  # TODO: Calculate from billing
            'growth_rate': 0.0,  # TODO: Calculate growth
        }

        serializer = PlatformMetricsSerializer(metrics)
        return Response(serializer.data)

    @action(detail=True, methods=['post'])
    def change_plan(self, request, pk=None):
        """
        Change a tenant's subscription plan.
        POST /api/platform/tenants/{id}/change_plan/

        Body: {"plan_code": "pro"}

        This updates the business's subscription to use the active version
        of the specified plan.
        """
        from smoothschedule.billing.models import Plan, Subscription

        tenant = self.get_object()
        plan_code = request.data.get('plan_code')

        if not plan_code:
            return Response(
                {"detail": "plan_code is required."},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Find the plan and its active version
        try:
            plan = Plan.objects.get(code=plan_code, is_active=True)
        except Plan.DoesNotExist:
            return Response(
                {"detail": f"Plan '{plan_code}' not found or not active."},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Get the active (non-legacy, public) version
        active_version = plan.versions.filter(
            is_legacy=False,
            is_public=True
        ).order_by('-version').first()

        if not active_version:
            return Response(
                {"detail": f"Plan '{plan_code}' has no active version available."},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Get or create subscription for this tenant
        from django.utils import timezone
        from datetime import timedelta
        now = timezone.now()
        subscription, created = Subscription.objects.get_or_create(
            business=tenant,
            defaults={
                'plan_version': active_version,
                'status': 'active',
                'current_period_start': now,
                'current_period_end': now + timedelta(days=30),
            }
        )

        if not created:
            # Update existing subscription
            old_plan_name = (
                subscription.plan_version.plan.name
                if subscription.plan_version else 'None'
            )
            subscription.plan_version = active_version
            subscription.save(update_fields=['plan_version', 'updated_at'])
        else:
            old_plan_name = 'None (new subscription)'

        return Response({
            "detail": f"Plan changed from {old_plan_name} to {plan.name}.",
            "plan_code": plan.code,
            "plan_name": plan.name,
            "version": active_version.version,
        })

    @action(detail=True, methods=['get', 'put', 'delete'], url_path='custom_tier')
    def custom_tier(self, request, pk=None):
        """
        Manage custom tier for a tenant.

        GET: Return the tenant's custom tier or 404 if none exists
        PUT: Create or update the tenant's custom tier
        DELETE: Delete the tenant's custom tier
        """
        tenant = self.get_object()

        if request.method == 'GET':
            try:
                custom_tier = TenantCustomTier.objects.get(business=tenant)
                serializer = TenantCustomTierSerializer(custom_tier)
                return Response(serializer.data)
            except TenantCustomTier.DoesNotExist:
                return Response(
                    {"detail": "No custom tier found for this tenant."},
                    status=status.HTTP_404_NOT_FOUND
                )

        elif request.method == 'PUT':
            # Get or create custom tier
            custom_tier, created = TenantCustomTier.objects.get_or_create(
                business=tenant,
                defaults={'created_by': request.user}
            )

            # Update the custom tier
            serializer = TenantCustomTierSerializer(
                custom_tier,
                data=request.data,
                partial=True
            )
            serializer.is_valid(raise_exception=True)
            serializer.save()

            return Response(
                serializer.data,
                status=status.HTTP_201_CREATED if created else status.HTTP_200_OK
            )

        elif request.method == 'DELETE':
            try:
                custom_tier = TenantCustomTier.objects.get(business=tenant)
                custom_tier.delete()
                return Response(
                    {"detail": "Custom tier deleted successfully."},
                    status=status.HTTP_204_NO_CONTENT
                )
            except TenantCustomTier.DoesNotExist:
                return Response(
                    {"detail": "No custom tier found for this tenant."},
                    status=status.HTTP_404_NOT_FOUND
                )


class PlatformUserViewSet(viewsets.ModelViewSet):
    """
    ViewSet for viewing and updating users across the platform.
    Platform admins only.
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = PlatformUserSerializer
    permission_classes = [IsAuthenticated, IsPlatformAdmin]
    http_method_names = ['get', 'post', 'patch', 'head', 'options']  # Allow GET, POST, and PATCH

    def get_queryset(self):
        """Optionally filter by business or role"""
        queryset = super().get_queryset()

        # Filter by role
        role = self.request.query_params.get('role')
        if role:
            queryset = queryset.filter(role=role)

        # Filter by active status
        is_active = self.request.query_params.get('is_active')
        if is_active is not None:
            queryset = queryset.filter(is_active=is_active.lower() == 'true')

        # TODO: Filter by business when we add tenant reference to User

        return queryset

    @action(detail=True, methods=['post'])
    def verify_email(self, request, pk=None):
        """Manually verify a user's email"""
        user = self.get_object()
        user.email_verified = True
        user.save(update_fields=['email_verified'])
        return Response({'status': 'email verified'})



    def partial_update(self, request, *args, **kwargs):
        """
        Update platform user.
        Superusers can update anyone.
        Platform managers can only update platform_support users.
        """
        instance = self.get_object()
        user = request.user

        # Permission check: superusers can edit anyone
        if user.role != User.Role.SUPERUSER:
            # Platform managers can only edit platform_support users
            if user.role == User.Role.PLATFORM_MANAGER:
                if instance.role != User.Role.PLATFORM_SUPPORT:
                    return Response(
                        {"detail": "You can only edit Platform Support users."},
                        status=status.HTTP_403_FORBIDDEN
                    )
            else:
                return Response(
                    {"detail": "You do not have permission to edit users."},
                    status=status.HTTP_403_FORBIDDEN
                )

        # Update user fields
        allowed_fields = ['username', 'email', 'first_name', 'last_name', 'is_active', 'role', 'permissions']

        for field in allowed_fields:
            if field in request.data:
                if field == 'role':
                    # Validate role - only allow platform roles
                    role_value = request.data[field].upper()
                    if role_value not in ['PLATFORM_MANAGER', 'PLATFORM_SUPPORT']:
                        return Response(
                            {"detail": "Invalid role. Only platform_manager and platform_support are allowed."},
                            status=status.HTTP_400_BAD_REQUEST
                        )
                    setattr(instance, field, role_value)
                elif field == 'permissions':
                    # Merge permissions - don't replace entirely
                    current_permissions = instance.permissions or {}
                    new_permissions = request.data[field]

                    # Only allow granting permissions that the current user has
                    for perm_key, perm_value in new_permissions.items():
                        if perm_key == 'can_approve_plugins':
                            # Only superusers or users with this permission can grant it
                            if user.role == User.Role.SUPERUSER or user.permissions.get('can_approve_plugins', False):
                                current_permissions[perm_key] = perm_value
                        elif perm_key == 'can_whitelist_urls':
                            # Only superusers or users with this permission can grant it
                            if user.role == User.Role.SUPERUSER or user.permissions.get('can_whitelist_urls', False):
                                current_permissions[perm_key] = perm_value

                    instance.permissions = current_permissions
                else:
                    setattr(instance, field, request.data[field])

        # Handle password update if provided
        if 'password' in request.data and request.data['password']:
            instance.set_password(request.data['password'])

        instance.save()

        serializer = self.get_serializer(instance)
        return Response(serializer.data)


class TenantInvitationViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing Tenant Invitations.
    Platform admins only for all actions except token-based retrieval and acceptance.
    """
    queryset = TenantInvitation.objects.all().order_by('-created_at')
    serializer_class = TenantInvitationSerializer
    permission_classes = [IsAuthenticated, IsPlatformAdmin]
    http_method_names = ['get', 'post', 'delete', 'head', 'options']

    def get_serializer_class(self):
        if self.action == 'create':
            return TenantInvitationCreateSerializer
        return TenantInvitationSerializer

    def perform_create(self, serializer):
        # The create method on the model will handle cancelling old invitations
        # and generating token/expires_at.
        instance = serializer.save(invited_by=self.request.user)
        # Send invitation email via Celery task
        from .tasks import send_tenant_invitation_email
        send_tenant_invitation_email.delay(instance.id)

    @action(detail=True, methods=['post'])
    def resend(self, request, pk=None):
        """Resend invitation email for a specific invitation."""
        invitation = self.get_object()
        if not invitation.is_valid():
            return Response(
                {"detail": "Invitation is not in a valid state to be resent."},
                status=status.HTTP_400_BAD_REQUEST
            )
        # Update expires_at and token for resend (optional, but good practice)
        invitation.expires_at = timezone.now() + timedelta(days=7)
        invitation.token = secrets.token_urlsafe(32) # Generate new token
        invitation.save()

        # Send invitation email via Celery task
        from .tasks import send_tenant_invitation_email
        send_tenant_invitation_email.delay(invitation.id)
        return Response({"detail": "Invitation email resent successfully."}, status=status.HTTP_200_OK)

    @action(detail=True, methods=['post'])
    def cancel(self, request, pk=None):
        """Cancel a pending invitation."""
        invitation = self.get_object()
        if invitation.status == TenantInvitation.Status.PENDING:
            invitation.cancel()
            return Response({"detail": "Invitation cancelled successfully."}, status=status.HTTP_200_OK)
        return Response(
            {"detail": "Only pending invitations can be cancelled."},
            status=status.HTTP_400_BAD_REQUEST
        )

    # Public actions (no authentication required, accessible via token)
    @action(detail=False, methods=['get'], url_path='token/(?P<token>[^/.]+)', permission_classes=[])
    def retrieve_by_token(self, request, token=None):
        """Retrieve invitation details using a public token."""
        try:
            invitation = TenantInvitation.objects.get(token=token)
        except TenantInvitation.DoesNotExist:
            return Response({"detail": "Invitation not found or invalid token."}, status=status.HTTP_404_NOT_FOUND)

        if not invitation.is_valid():
            return Response({"detail": "Invitation is no longer valid."}, status=status.HTTP_400_BAD_REQUEST)

        serializer = TenantInvitationDetailSerializer(invitation)
        return Response(serializer.data)

    @action(detail=False, methods=['post'], url_path='token/(?P<token>[^/.]+)/accept', permission_classes=[])
    def accept(self, request, token=None):
        """Accept an invitation, create tenant and owner user."""
        try:
            invitation = TenantInvitation.objects.get(token=token)
        except TenantInvitation.DoesNotExist:
            return Response({"detail": "Invitation not found or invalid token."}, status=status.HTTP_404_NOT_FOUND)

        if not invitation.is_valid():
            return Response({"detail": "Invitation is no longer valid."}, status=status.HTTP_400_BAD_REQUEST)

        serializer = TenantInvitationAcceptSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # Force execution in public schema for tenant creation
        with schema_context('public'):
            with transaction.atomic():
                # Create Tenant (permissions now handled via billing.Subscription)
                subdomain = serializer.validated_data['subdomain'].lower()
                tenant = Tenant.objects.create(
                    schema_name=subdomain,
                    name=serializer.validated_data['business_name'],
                    contact_email=serializer.validated_data.get('contact_email', invitation.email),
                    phone=serializer.validated_data.get('phone', ''),
                )

                # Create billing subscription based on invitation tier
                from smoothschedule.billing.models import Plan, Subscription
                from django.utils import timezone as dj_timezone
                from datetime import timedelta

                # Map old tier names to new plan codes
                tier_to_plan = {
                    'FREE': 'free',
                    'STARTER': 'starter',
                    'PROFESSIONAL': 'growth',  # Map old Professional to Growth
                    'ENTERPRISE': 'enterprise',
                }
                plan_code = tier_to_plan.get(invitation.subscription_tier, 'free')

                try:
                    plan = Plan.objects.get(code=plan_code, is_active=True)
                    plan_version = plan.versions.filter(
                        is_public=True,
                        is_legacy=False,
                    ).order_by('-version').first()

                    if plan_version:
                        now = dj_timezone.now()
                        Subscription.objects.create(
                            business=tenant,
                            plan_version=plan_version,
                            status='active' if plan_code == 'free' else 'trial',
                            current_period_start=now,
                            current_period_end=now + timedelta(days=30),
                            trial_ends_at=now + timedelta(days=14) if plan_code != 'free' else None,
                        )
                except Plan.DoesNotExist:
                    pass  # No subscription created - tenant will have no plan

                # Create primary domain
                domain_name = f"{subdomain}.lvh.me"  # TODO: Make base domain configurable
                Domain.objects.create(
                    domain=domain_name,
                    tenant=tenant,
                    is_primary=True,
                    is_custom_domain=False,
                )

                # Create Owner User
                # Email is verified since they received the invitation email
                owner_user = User.objects.create_user(
                    username=serializer.validated_data['email'],
                    email=serializer.validated_data['email'],
                    password=serializer.validated_data['password'],
                    first_name=serializer.validated_data['first_name'],
                    last_name=serializer.validated_data['last_name'],
                    role=User.Role.TENANT_OWNER,
                    tenant=tenant,
                    email_verified=True,
                )

                # Mark invitation as accepted
                invitation.accept(tenant, owner_user)

        return Response({"detail": "Invitation accepted, tenant and user created."}, status=status.HTTP_201_CREATED)


class PlatformEmailAddressViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing platform email addresses.
    These are email addresses hosted on mail.talova.net that are
    managed directly via SSH commands to the mail server.

    Platform admins only.
    """
    queryset = PlatformEmailAddress.objects.all().order_by('-is_default', 'display_name')
    permission_classes = [IsAuthenticated, IsPlatformAdmin]

    def get_serializer_class(self):
        if self.action == 'list':
            return PlatformEmailAddressListSerializer
        if self.action == 'create':
            return PlatformEmailAddressCreateSerializer
        if self.action in ['update', 'partial_update']:
            return PlatformEmailAddressUpdateSerializer
        return PlatformEmailAddressSerializer

    def perform_destroy(self, instance):
        """Delete email address from both database and mail server."""
        from .mail_server import get_mail_server_service

        # Delete from mail server first
        service = get_mail_server_service()
        success, message = service.delete_and_unsync(instance)

        if not success:
            # Log the error but still delete from database
            import logging
            logger = logging.getLogger(__name__)
            logger.warning(f"Failed to delete email from mail server: {message}")

        # Delete from database
        instance.delete()

    @action(detail=True, methods=['post'])
    def sync(self, request, pk=None):
        """
        Manually sync this email address to the mail server.
        Creates the account if it doesn't exist, or updates the password if it does.
        """
        from .mail_server import get_mail_server_service

        email_address = self.get_object()
        service = get_mail_server_service()

        success, message = service.sync_account(email_address)

        if success:
            return Response({
                'success': True,
                'message': message,
                'mail_server_synced': email_address.mail_server_synced,
                'last_synced_at': email_address.last_synced_at,
            })
        else:
            return Response({
                'success': False,
                'message': message,
                'mail_server_synced': email_address.mail_server_synced,
                'last_sync_error': email_address.last_sync_error,
            }, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=['post'])
    def set_as_default(self, request, pk=None):
        """Set this email address as the default for platform support."""
        email_address = self.get_object()

        # Unset all other defaults
        PlatformEmailAddress.objects.filter(
            is_default=True
        ).exclude(pk=email_address.pk).update(is_default=False)

        # Set this one as default
        email_address.is_default = True
        email_address.save()

        return Response({
            'success': True,
            'message': f'{email_address.display_name} is now the default email address',
        })

    @action(detail=True, methods=['post'])
    def test_imap(self, request, pk=None):
        """Test IMAP connection for this email address."""
        import imaplib

        email_address = self.get_object()
        settings = email_address.get_imap_settings()

        try:
            if settings['use_ssl']:
                imap = imaplib.IMAP4_SSL(settings['host'], settings['port'])
            else:
                imap = imaplib.IMAP4(settings['host'], settings['port'])

            imap.login(settings['username'], settings['password'])
            imap.select(settings['folder'])
            imap.logout()

            return Response({
                'success': True,
                'message': 'IMAP connection successful',
            })
        except Exception as e:
            return Response({
                'success': False,
                'message': f'IMAP connection failed: {str(e)}',
            }, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=['post'])
    def test_smtp(self, request, pk=None):
        """Test SMTP connection for this email address."""
        import smtplib

        email_address = self.get_object()
        settings = email_address.get_smtp_settings()

        try:
            if settings['use_ssl']:
                smtp = smtplib.SMTP_SSL(settings['host'], settings['port'])
            else:
                smtp = smtplib.SMTP(settings['host'], settings['port'])
                if settings['use_tls']:
                    smtp.starttls()

            smtp.login(settings['username'], settings['password'])
            smtp.quit()

            return Response({
                'success': True,
                'message': 'SMTP connection successful',
            })
        except Exception as e:
            return Response({
                'success': False,
                'message': f'SMTP connection failed: {str(e)}',
            }, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=['post'])
    def test_mail_server(self, request):
        """Test SSH connection to the mail server."""
        from .mail_server import get_mail_server_service

        service = get_mail_server_service()
        success, message = service.test_connection()

        if success:
            return Response({
                'success': True,
                'message': message,
            })
        else:
            return Response({
                'success': False,
                'message': message,
            }, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=['get'])
    def mail_server_accounts(self, request):
        """List all email accounts on the mail server."""
        from .mail_server import get_mail_server_service, MailServerError

        service = get_mail_server_service()

        try:
            accounts = service.list_accounts()
            return Response({
                'success': True,
                'accounts': accounts,
                'count': len(accounts),
            })
        except MailServerError as e:
            return Response({
                'success': False,
                'message': str(e),
            }, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=['get'])
    def available_domains(self, request):
        """Get available email domains."""
        domains = [
            {'value': choice[0], 'label': choice[1]}
            for choice in PlatformEmailAddress.Domain.choices
        ]
        return Response({
            'domains': domains,
        })

    @action(detail=False, methods=['get'])
    def assignable_users(self, request):
        """Get users that can be assigned to email addresses."""
        from smoothschedule.identity.users.models import User

        users = User.objects.filter(
            role__in=['superuser', 'platform_manager', 'platform_support'],
            is_active=True
        ).order_by('first_name', 'last_name', 'email')

        user_list = [
            {
                'id': user.id,
                'email': user.email,
                'first_name': user.first_name,
                'last_name': user.last_name,
                'full_name': user.get_full_name() or user.email,
                'role': user.role,
            }
            for user in users
        ]

        return Response({
            'users': user_list,
        })

    @action(detail=True, methods=['post'])
    def remove_local(self, request, pk=None):
        """
        Remove email address from database only, without deleting from mail server.
        Useful for removing an address from the platform while keeping
        the mail server account intact.
        """
        email_address = self.get_object()
        email = email_address.email_address
        display_name = email_address.display_name

        # Just delete from database, don't touch mail server
        email_address.delete()

        return Response({
            'success': True,
            'message': f'Removed {display_name} ({email}) from database. Account still exists on mail server.',
        })

    @action(detail=False, methods=['post'])
    def import_from_mail_server(self, request):
        """
        Import existing email accounts from the mail server.
        Only imports accounts with supported domains that don't already exist in the database.
        """
        from .mail_server import get_mail_server_service, MailServerError
        import secrets

        service = get_mail_server_service()

        try:
            accounts = service.list_accounts()
        except MailServerError as e:
            return Response({
                'success': False,
                'message': str(e),
            }, status=status.HTTP_400_BAD_REQUEST)

        # Only import smoothschedule.com addresses
        supported_domains = ['smoothschedule.com']

        # Get existing email addresses (construct from local_part + domain)
        existing_emails = set(
            f"{addr.local_part}@{addr.domain}".lower()
            for addr in PlatformEmailAddress.objects.only('local_part', 'domain')
        )

        imported = []
        skipped = []

        for account in accounts:
            email = account.get('email', '')
            if not email or '@' not in email:
                continue

            local_part, domain = email.rsplit('@', 1)

            # Skip if domain not supported
            if domain not in supported_domains:
                skipped.append({
                    'email': email,
                    'reason': 'Unsupported domain',
                })
                continue

            # Skip if already exists
            if email.lower() in existing_emails:
                skipped.append({
                    'email': email,
                    'reason': 'Already exists in database',
                })
                continue

            # Create the email address with a placeholder password
            # User will need to update the password to sync properly
            placeholder_password = secrets.token_urlsafe(16)

            try:
                email_address = PlatformEmailAddress.objects.create(
                    display_name=local_part.title().replace('.', ' ').replace('-', ' '),
                    local_part=local_part,
                    domain=domain,
                    password=placeholder_password,
                    is_active=True,
                    is_default=False,
                    mail_server_synced=True,  # Already exists on server
                )
                imported.append({
                    'id': email_address.id,
                    'email': email_address.email_address,
                    'display_name': email_address.display_name,
                })
            except Exception as e:
                skipped.append({
                    'email': email,
                    'reason': str(e),
                })

        return Response({
            'success': True,
            'imported': imported,
            'imported_count': len(imported),
            'skipped': skipped,
            'skipped_count': len(skipped),
            'message': f'Imported {len(imported)} email addresses, skipped {len(skipped)}',
        })
