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
from .serializers import (
    BroadcastMessageListSerializer,
    BroadcastMessageDetailSerializer,
    BroadcastMessageCreateSerializer,
    InboxMessageSerializer,
    MessageRecipientSerializer
)

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})
