import logging
from django.utils import timezone
from django.db.models import Q
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, BasePermission
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

from smoothschedule.identity.users.models import User
from .models import BroadcastMessage, MessageRecipient, PuckEmailTemplate, CustomEmailTemplate
from .email_types import EmailType
from .email_tags import get_tag_info_for_email_type, get_all_tag_info
from .serializers import (
    BroadcastMessageListSerializer,
    BroadcastMessageDetailSerializer,
    BroadcastMessageCreateSerializer,
    InboxMessageSerializer,
    MessageRecipientSerializer,
    PuckEmailTemplateListSerializer,
    PuckEmailTemplateDetailSerializer,
    EmailTemplatePreviewSerializer,
    CustomEmailTemplateListSerializer,
    CustomEmailTemplateDetailSerializer,
    CustomEmailTemplateCreateSerializer,
    CustomEmailTemplateUpdateSerializer,
)

logger = logging.getLogger(__name__)


class CanSendMessagesPermission(BasePermission):
    """
    Permission check for sending broadcast messages.
    Only owners and managers (with permission) can send.
    """
    message = "You do not have permission to send messages."

    def has_permission(self, request, view):
        if not request.user.is_authenticated:
            return False
        # Read operations allowed for anyone authenticated
        if request.method in ['GET', 'HEAD', 'OPTIONS']:
            return True
        # Write operations require can_send_messages
        return request.user.can_send_messages()


def send_websocket_notification(user_id, message_data):
    """Send a WebSocket notification to a user's notification group."""
    try:
        channel_layer = get_channel_layer()
        if channel_layer is None:
            logger.warning("Channel layer not configured, skipping WebSocket notification")
            return

        group_name = f'user_{user_id}'
        async_to_sync(channel_layer.group_send)(
            group_name,
            {
                "type": "notification_message",
                "message": message_data
            }
        )
    except Exception as e:
        logger.error(f"Failed to send WebSocket notification to user {user_id}: {e}")


def create_notification_for_user(recipient, message):
    """Create a notification record for a broadcast message."""
    try:
        from smoothschedule.communication.notifications.models import Notification
        from django.contrib.contenttypes.models import ContentType

        # Create notification
        notification = Notification.objects.create(
            recipient=recipient,
            actor=message.sender,
            verb=f'sent you a message: "{message.subject}"',
            action_object=message,
            data={
                'message_id': message.id,
                'subject': message.subject,
                'type': 'broadcast_message'
            }
        )
        return notification
    except Exception as e:
        logger.error(f"Failed to create notification for user {recipient.id}: {e}")
        return None


class BroadcastMessageViewSet(viewsets.ModelViewSet):
    """
    API endpoint for managing broadcast messages.

    Owners and managers can create and send messages to groups of users.
    """
    permission_classes = [IsAuthenticated, CanSendMessagesPermission]

    def get_queryset(self):
        """Return messages sent by the current user or their tenant."""
        user = self.request.user
        # For now, return messages where user is the sender
        return BroadcastMessage.objects.filter(sender=user).select_related('sender')

    def get_serializer_class(self):
        if self.action == 'list':
            return BroadcastMessageListSerializer
        elif self.action == 'create':
            return BroadcastMessageCreateSerializer
        return BroadcastMessageDetailSerializer

    def perform_create(self, serializer):
        """Set the sender to the current user."""
        serializer.save(sender=self.request.user)

    @action(detail=True, methods=['post'])
    def send(self, request, pk=None):
        """
        Send the message to all targeted recipients.
        Creates notifications and sends WebSocket updates.
        """
        message = self.get_object()

        if message.status == BroadcastMessage.Status.SENT:
            return Response(
                {'error': 'Message has already been sent'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Get all targeted recipients
        recipients = self._get_target_recipients(message)

        if not recipients:
            return Response(
                {'error': 'No recipients found for the selected targets'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Update message status
        message.status = BroadcastMessage.Status.SENDING
        message.save(update_fields=['status'])

        # Create recipient records and send notifications
        created_count = 0
        for user in recipients:
            # Create recipient record
            recipient, created = MessageRecipient.objects.get_or_create(
                message=message,
                user=user,
                defaults={'delivery_status': MessageRecipient.DeliveryStatus.PENDING}
            )

            if created:
                created_count += 1

                # Create in-app notification
                notification = create_notification_for_user(user, message)

                # Send WebSocket notification for immediate display
                send_websocket_notification(user.id, {
                    'type': 'broadcast_message',
                    'message_id': message.id,
                    'subject': message.subject,
                    'sender': message.sender.get_full_name() if message.sender else 'System',
                    'preview': message.body[:100] + '...' if len(message.body) > 100 else message.body,
                    'timestamp': timezone.now().isoformat()
                })

                # Mark as delivered for in-app notifications
                if message.delivery_method in [BroadcastMessage.DeliveryMethod.IN_APP, BroadcastMessage.DeliveryMethod.ALL]:
                    recipient.delivery_status = MessageRecipient.DeliveryStatus.DELIVERED
                    recipient.delivered_at = timezone.now()
                    recipient.save(update_fields=['delivery_status', 'delivered_at'])

        # Update message statistics
        message.total_recipients = created_count
        message.delivered_count = MessageRecipient.objects.filter(
            message=message,
            delivery_status=MessageRecipient.DeliveryStatus.DELIVERED
        ).count()
        message.status = BroadcastMessage.Status.SENT
        message.sent_at = timezone.now()
        message.save(update_fields=['total_recipients', 'delivered_count', 'status', 'sent_at'])

        return Response({
            'status': 'sent',
            'total_recipients': message.total_recipients,
            'delivered_count': message.delivered_count
        })

    def _get_target_recipients(self, message):
        """Get all users that match the message targets."""
        user = self.request.user
        tenant = user.tenant

        if not tenant:
            return []

        # Build query for targeted role groups
        role_filters = Q()

        if message.target_owners:
            role_filters |= Q(role=User.Role.TENANT_OWNER)
        if message.target_managers:
            role_filters |= Q(role=User.Role.TENANT_MANAGER)
        if message.target_staff:
            role_filters |= Q(role=User.Role.TENANT_STAFF)
        if message.target_customers:
            role_filters |= Q(role=User.Role.CUSTOMER)

        # Get users from role groups (same tenant)
        recipients = set()
        if role_filters:
            tenant_users = User.objects.filter(
                tenant=tenant,
                is_active=True
            ).filter(role_filters).exclude(id=user.id)  # Don't send to self
            recipients.update(tenant_users)

        # Add individual recipients
        for individual in message.individual_recipients.all():
            if individual.id != user.id:  # Don't send to self
                recipients.add(individual)

        return list(recipients)

    @action(detail=True, methods=['get'])
    def recipients(self, request, pk=None):
        """Get delivery status for all recipients of a message."""
        message = self.get_object()
        recipients = message.recipients.all().select_related('user')
        serializer = MessageRecipientSerializer(recipients, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=['get'])
    def recipient_options(self, request):
        """
        Get available recipient options for the message composer.
        Returns counts for each group and list of individual users.
        """
        user = request.user
        tenant = user.tenant

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

        # Get counts for each role group
        base_query = User.objects.filter(tenant=tenant, is_active=True).exclude(id=user.id)

        owner_count = base_query.filter(role=User.Role.TENANT_OWNER).count()
        manager_count = base_query.filter(role=User.Role.TENANT_MANAGER).count()
        staff_count = base_query.filter(role=User.Role.TENANT_STAFF).count()
        customer_count = base_query.filter(role=User.Role.CUSTOMER).count()

        # Get list of individual users for selection
        users_queryset = base_query.order_by('first_name', 'last_name')
        users = [
            {
                'id': str(u.id),
                'name': u.full_name or u.email,
                'email': u.email,
                'role': u.get_role_display().replace('Tenant ', '').lower(),
            }
            for u in users_queryset
        ]

        return Response({
            'groups': {
                'owners': {'count': owner_count, 'label': 'All Owners'},
                'managers': {'count': manager_count, 'label': 'All Managers'},
                'staff': {'count': staff_count, 'label': 'All Staff'},
                'customers': {'count': customer_count, 'label': 'All Customers'},
            },
            'users': users
        })


class InboxViewSet(viewsets.ReadOnlyModelViewSet):
    """
    API endpoint for user's message inbox.
    Shows broadcast messages received by the current user.
    """
    serializer_class = InboxMessageSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        """Return messages where the current user is a recipient."""
        user = self.request.user
        # Get message IDs where user is a recipient
        recipient_message_ids = MessageRecipient.objects.filter(
            user=user
        ).values_list('message_id', flat=True)

        return BroadcastMessage.objects.filter(
            id__in=recipient_message_ids,
            status=BroadcastMessage.Status.SENT
        ).select_related('sender').order_by('-sent_at')

    @action(detail=True, methods=['post'])
    def mark_read(self, request, pk=None):
        """Mark a message as read for the current user."""
        message = self.get_object()
        recipient = MessageRecipient.objects.filter(
            message=message,
            user=request.user
        ).first()

        if not recipient:
            return Response(
                {'error': 'Message not found in inbox'},
                status=status.HTTP_404_NOT_FOUND
            )

        if not recipient.is_read:
            recipient.is_read = True
            recipient.read_at = timezone.now()
            recipient.save(update_fields=['is_read', 'read_at'])

            # Update message read count
            message.read_count = MessageRecipient.objects.filter(
                message=message,
                is_read=True
            ).count()
            message.save(update_fields=['read_count'])

        return Response({'status': 'marked as read'})

    @action(detail=False, methods=['post'])
    def mark_all_read(self, request):
        """Mark all inbox messages as read."""
        user = request.user
        now = timezone.now()

        updated = MessageRecipient.objects.filter(
            user=user,
            is_read=False
        ).update(is_read=True, read_at=now)

        return Response({'status': f'marked {updated} messages as read'})

    @action(detail=False, methods=['get'])
    def unread_count(self, request):
        """Get count of unread messages."""
        count = MessageRecipient.objects.filter(
            user=request.user,
            is_read=False,
            message__status=BroadcastMessage.Status.SENT
        ).count()
        return Response({'count': count})


# =============================================================================
# Email Template Views
# =============================================================================

class IsOwnerOrManager(BasePermission):
    """Only owners and managers can manage email templates."""
    message = "You must be an owner or manager to manage email templates."

    def has_permission(self, request, view):
        if not request.user.is_authenticated:
            return False
        return request.user.role in [
            User.Role.TENANT_OWNER,
            User.Role.TENANT_MANAGER,
        ]


class EmailTemplateViewSet(viewsets.ModelViewSet):
    """
    API endpoint for managing Puck-based email templates.

    Templates are tenant-scoped (one per email type per tenant).
    Supports viewing, updating, and resetting templates.
    """
    permission_classes = [IsAuthenticated, IsOwnerOrManager]
    lookup_field = 'email_type'
    lookup_value_regex = '[a-z_]+'

    def get_queryset(self):
        """Return all templates for the current tenant."""
        return PuckEmailTemplate.objects.all()

    def get_serializer_class(self):
        if self.action == 'list':
            return PuckEmailTemplateListSerializer
        return PuckEmailTemplateDetailSerializer

    def get_object(self):
        """
        Get or create template for the specified email_type.

        This ensures every email type has a template available.
        """
        email_type_value = self.kwargs.get(self.lookup_field)

        try:
            email_type = EmailType(email_type_value)
        except ValueError:
            from rest_framework.exceptions import NotFound
            raise NotFound(f"Invalid email type: {email_type_value}")

        template = PuckEmailTemplate.get_or_create_for_type(email_type)
        return template

    def list(self, request, *args, **kwargs):
        """
        List all email templates, creating defaults for missing types.
        """
        # Ensure all email types have templates
        for email_type in EmailType:
            PuckEmailTemplate.get_or_create_for_type(email_type)

        return super().list(request, *args, **kwargs)

    def create(self, request, *args, **kwargs):
        """
        Create is not allowed - templates are created automatically.
        """
        return Response(
            {'error': 'Templates are created automatically. Use PUT to update.'},
            status=status.HTTP_405_METHOD_NOT_ALLOWED
        )

    def destroy(self, request, *args, **kwargs):
        """
        Delete is not allowed - use reset instead.
        """
        return Response(
            {'error': 'Templates cannot be deleted. Use POST /{email_type}/reset to restore defaults.'},
            status=status.HTTP_405_METHOD_NOT_ALLOWED
        )

    @action(detail=True, methods=['post'])
    def reset(self, request, email_type=None):
        """
        Reset a template to its default content.
        """
        template = self.get_object()
        template.reset_to_default()
        serializer = self.get_serializer(template)
        return Response(serializer.data)

    @action(detail=True, methods=['post'])
    def preview(self, request, email_type=None):
        """
        Preview rendered email with sample data.

        Accepts optional context dict with tag values for rendering.
        Returns rendered subject, html, and text.
        """
        template = self.get_object()

        # Parse preview context
        preview_serializer = EmailTemplatePreviewSerializer(data=request.data)
        preview_serializer.is_valid(raise_exception=True)
        context = preview_serializer.validated_data.get('context', {})

        # Add sample values for missing tags
        sample_context = self._get_sample_context(template.email_type)
        sample_context.update(context)

        # Check white-label permission from tenant
        tenant = getattr(request, 'tenant', None)
        if tenant and tenant.has_feature('can_white_label'):
            sample_context['can_remove_branding'] = True

        # Render the template
        rendered = template.render(sample_context)

        return Response({
            'subject': rendered['subject'],
            'html': rendered['html'],
            'text': rendered['text'],
        })

    def _get_sample_context(self, email_type_value: str) -> dict:
        """Generate sample context for preview."""
        return {
            # Business info
            'business_name': 'Acme Business',
            'business_email': 'contact@acme.com',
            'business_phone': '(555) 123-4567',
            'business_address': '123 Main St, City, ST 12345',
            'business_logo_url': '',
            'business_website_url': 'https://acme.com',

            # Customer info
            'customer_name': 'John Doe',
            'customer_first_name': 'John',
            'customer_last_name': 'Doe',
            'customer_email': 'john@example.com',
            'customer_phone': '(555) 987-6543',

            # Date/time
            'current_date': 'December 15, 2024',
            'current_year': '2024',

            # Appointment
            'appointment_date': 'December 20, 2024',
            'appointment_time': '2:00 PM',
            'appointment_datetime': 'December 20, 2024 at 2:00 PM',
            'appointment_duration': '1 hour',
            'service_name': 'Consultation',
            'service_description': 'Initial consultation meeting',
            'staff_name': 'Jane Smith',
            'location_name': 'Main Office',
            'location_address': '123 Main St, Suite 100',
            'manage_appointment_link': 'https://example.com/manage/123',
            'reschedule_link': 'https://example.com/reschedule/123',
            'cancel_link': 'https://example.com/cancel/123',
            'add_to_calendar_link': 'https://example.com/calendar/123.ics',

            # Contract
            'contract_title': 'Service Agreement',
            'contract_description': 'Standard service agreement terms',
            'signing_link': 'https://example.com/sign/123',
            'contract_expires_at': 'January 1, 2025',
            'days_until_expiry': '7',
            'signed_at': 'December 15, 2024 at 3:00 PM',

            # Payment
            'amount_due': '$150.00',
            'amount_paid': '$150.00',
            'currency': 'USD',
            'invoice_number': 'INV-2024-001',
            'receipt_link': 'https://example.com/receipt/123',
            'payment_link': 'https://example.com/pay/123',
            'payment_due_date': 'December 31, 2024',

            # Ticket
            'ticket_id': 'TKT-001',
            'ticket_subject': 'Help with booking',
            'ticket_status': 'Open',
            'ticket_priority': 'Normal',
            'ticket_message': 'I need help with my booking...',
            'reply_message': 'Thank you for contacting us...',
            'ticket_link': 'https://example.com/ticket/123',
            'assignee_name': 'Support Team',

            # Extra
            'deposit_amount': '$50.00',
            'total_paid': '$150.00',
            'review_link': 'https://example.com/review/123',
        }


class EmailTemplateTagsView(viewsets.ViewSet):
    """
    API endpoint for retrieving available template tags.
    """
    permission_classes = [IsAuthenticated, IsOwnerOrManager]

    def list(self, request):
        """
        List all email types with their available tags.
        """
        result = {}
        for email_type in EmailType:
            result[email_type.value] = {
                'display_name': EmailType.get_display_name(email_type),
                'description': EmailType.get_description(email_type),
                'category': EmailType.get_category(email_type),
                'tags': get_tag_info_for_email_type(email_type),
            }
        return Response(result)

    @action(detail=False, methods=['get'])
    def by_type(self, request):
        """
        Get tags for a specific email type.

        Query parameter: email_type
        """
        email_type_value = request.query_params.get('email_type')
        if not email_type_value:
            return Response(
                {'error': 'email_type query parameter is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            email_type = EmailType(email_type_value)
        except ValueError:
            return Response(
                {'error': f'Invalid email type: {email_type_value}'},
                status=status.HTTP_400_BAD_REQUEST
            )

        return Response({
            'email_type': email_type.value,
            'display_name': EmailType.get_display_name(email_type),
            'description': EmailType.get_description(email_type),
            'category': EmailType.get_category(email_type),
            'tags': get_tag_info_for_email_type(email_type),
        })


# =============================================================================
# Custom Email Template Views (for plugins)
# =============================================================================

class CustomEmailTemplateViewSet(viewsets.ModelViewSet):
    """
    API endpoint for managing custom email templates.

    Custom templates are created by tenants for use with plugins.
    They have access to ALL template tags (not restricted by email type).
    """
    permission_classes = [IsAuthenticated, IsOwnerOrManager]

    def get_queryset(self):
        """Return all custom templates for the current tenant."""
        return CustomEmailTemplate.objects.all()

    def get_serializer_class(self):
        if self.action == 'list':
            return CustomEmailTemplateListSerializer
        elif self.action == 'create':
            return CustomEmailTemplateCreateSerializer
        elif self.action in ['update', 'partial_update']:
            return CustomEmailTemplateUpdateSerializer
        return CustomEmailTemplateDetailSerializer

    def perform_create(self, serializer):
        """Set created_by to current user."""
        serializer.save(created_by=self.request.user)

    @action(detail=True, methods=['post'])
    def preview(self, request, pk=None):
        """
        Preview rendered email with sample data.

        Accepts optional context dict with tag values for rendering.
        Returns rendered subject, html, and text.
        """
        template = self.get_object()

        # Parse preview context
        preview_serializer = EmailTemplatePreviewSerializer(data=request.data)
        preview_serializer.is_valid(raise_exception=True)
        context = preview_serializer.validated_data.get('context', {})

        # Add sample values for missing tags
        sample_context = self._get_sample_context()
        sample_context.update(context)

        # Check white-label permission from tenant
        tenant = getattr(request, 'tenant', None)
        if tenant and tenant.has_feature('can_white_label'):
            sample_context['can_remove_branding'] = True

        # Render the template
        rendered = template.render(sample_context)

        return Response({
            'subject': rendered['subject'],
            'html': rendered['html'],
            'text': rendered['text'],
        })

    @action(detail=True, methods=['post'])
    def duplicate(self, request, pk=None):
        """
        Create a copy of an existing template.
        """
        original = self.get_object()

        # Generate unique slug
        base_slug = f"{original.slug}-copy"
        slug = base_slug
        counter = 1
        while CustomEmailTemplate.objects.filter(slug=slug).exists():
            slug = f"{base_slug}-{counter}"
            counter += 1

        # Create the duplicate
        duplicate = CustomEmailTemplate.objects.create(
            slug=slug,
            name=f"{original.name} (Copy)",
            description=original.description,
            subject_template=original.subject_template,
            puck_data=original.puck_data,
            is_active=False,  # Start as inactive
            created_by=request.user,
        )

        serializer = CustomEmailTemplateDetailSerializer(duplicate)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    @action(detail=False, methods=['get'])
    def available_tags(self, request):
        """
        Get all available template tags for custom templates.

        Custom templates have access to ALL tags since they may be
        triggered from any context (plugins, etc).
        """
        return Response({
            'tags': get_all_tag_info()
        })

    def _get_sample_context(self) -> dict:
        """Generate sample context for preview with all possible tags."""
        return {
            # Business info
            'business_name': 'Acme Business',
            'business_email': 'contact@acme.com',
            'business_phone': '(555) 123-4567',
            'business_address': '123 Main St, City, ST 12345',
            'business_logo_url': '',
            'business_website_url': 'https://acme.com',

            # Customer info
            'customer_name': 'John Doe',
            'customer_first_name': 'John',
            'customer_last_name': 'Doe',
            'customer_email': 'john@example.com',
            'customer_phone': '(555) 987-6543',

            # Date/time
            'current_date': 'December 15, 2024',
            'current_year': '2024',

            # Appointment
            'appointment_date': 'December 20, 2024',
            'appointment_time': '2:00 PM',
            'appointment_datetime': 'December 20, 2024 at 2:00 PM',
            'appointment_duration': '1 hour',
            'service_name': 'Consultation',
            'service_description': 'Initial consultation meeting',
            'staff_name': 'Jane Smith',
            'location_name': 'Main Office',
            'location_address': '123 Main St, Suite 100',
            'manage_appointment_link': 'https://example.com/manage/123',
            'reschedule_link': 'https://example.com/reschedule/123',
            'cancel_link': 'https://example.com/cancel/123',
            'add_to_calendar_link': 'https://example.com/calendar/123.ics',

            # Contract
            'contract_title': 'Service Agreement',
            'contract_description': 'Standard service agreement terms',
            'signing_link': 'https://example.com/sign/123',
            'contract_expires_at': 'January 1, 2025',
            'days_until_expiry': '7',
            'signed_at': 'December 15, 2024 at 3:00 PM',

            # Payment
            'amount_due': '$150.00',
            'amount_paid': '$150.00',
            'currency': 'USD',
            'invoice_number': 'INV-2024-001',
            'receipt_link': 'https://example.com/receipt/123',
            'payment_link': 'https://example.com/pay/123',
            'payment_due_date': 'December 31, 2024',

            # Ticket
            'ticket_id': 'TKT-001',
            'ticket_subject': 'Help with booking',
            'ticket_status': 'Open',
            'ticket_priority': 'Normal',
            'ticket_message': 'I need help with my booking...',
            'reply_message': 'Thank you for contacting us...',
            'ticket_link': 'https://example.com/ticket/123',
            'assignee_name': 'Support Team',

            # Extra
            'deposit_amount': '$50.00',
            'total_paid': '$150.00',
            'review_link': 'https://example.com/review/123',
        }
