from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.contrib.auth import get_user_model
from django.db.models import Q
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, extend_schema_view

from vontor_cz.pagination import CreatedCursorPagination
from .models import Chat, Message, MessageFile
from .permissions import CanDeleteMessage, CanManageChat, IsChatMember, IsMessageSenderOnly
from .serializers import ChatMemberSerializer, ChatSerializer, MessageSendSerializer, MessageSerializer


def _broadcast(chat_id, payload):
    """Push a payload to the channel group for a chat from sync code."""
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(f"chat_{chat_id}", payload)


# ---------------------------------------------------------------------------
# Chat ViewSet
# ---------------------------------------------------------------------------

@extend_schema_view(
    list=extend_schema(tags=["chat"], summary="List chats", description="Returns all chats the user is a member of. Superusers see all."),
    retrieve=extend_schema(tags=["chat"], summary="Retrieve a chat"),
    create=extend_schema(tags=["chat"], summary="Create a chat", description="Owner is set to the requesting user automatically."),
    update=extend_schema(tags=["chat"], summary="Replace a chat", description="Owner, moderator, or superuser only."),
    partial_update=extend_schema(tags=["chat"], summary="Update a chat", description="Owner, moderator, or superuser only."),
    destroy=extend_schema(tags=["chat"], summary="Delete a chat", description="Soft-deletes. Owner or superuser only."),
)
class ChatViewSet(viewsets.ModelViewSet):
    serializer_class = ChatSerializer
    filterset_fields = ['chat_type', 'hub']
    search_fields = ['name']
    ordering_fields = ['created_at', 'name']
    ordering = ['-created_at']

    def get_permissions(self):
        if self.action in ('update', 'partial_update', 'destroy',
                           'add_member', 'remove_member', 'add_moderator', 'remove_moderator'):
            return [CanManageChat()]
        return [IsChatMember()]

    def get_queryset(self):
        user = self.request.user
        if user.is_superuser:
            return Chat.objects.all()
        return Chat.objects.filter(Q(members=user) | Q(owner=user)).distinct()

    def perform_create(self, serializer):
        chat = serializer.save(owner=self.request.user)
        
        # Ensure the creator is always a member so they pass membership checks.
        chat.members.add(self.request.user)

        if chat.chat_type == Chat.ChatType.DM:
            other = chat.members.exclude(pk=self.request.user.pk).first()
            if other:
                update_fields = []
                if not chat.name:
                    chat.name = other.username
                    update_fields.append('name')
                if not chat.icon and other.avatar:
                    chat.icon = other.avatar
                    update_fields.append('icon')
                if update_fields:
                    chat.save(update_fields=update_fields)

    # ------------------------------------------------------------------
    # Member management
    # ------------------------------------------------------------------

    @extend_schema(
        tags=["chat"],
        summary="Add a member",
        description="Owner, moderator, or superuser only.",
        request=ChatMemberSerializer,
        responses={200: ChatSerializer},
    )
    @action(detail=True, methods=['post'], url_path='members/add')
    def add_member(self, request, pk=None):
        chat = self.get_object()
        ser = ChatMemberSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        User = get_user_model()
        try:
            user = User.objects.get(pk=ser.validated_data['user_id'])
        except User.DoesNotExist:
            return Response({'detail': 'User not found.'}, status=status.HTTP_404_NOT_FOUND)
        chat.members.add(user)
        return Response(ChatSerializer(chat, context={'request': request}).data)

    @extend_schema(
        tags=["chat"],
        summary="Remove a member",
        description="Owner, moderator, or superuser only.",
        request=ChatMemberSerializer,
        responses={204: None},
    )
    @action(detail=True, methods=['post'], url_path='members/remove')
    def remove_member(self, request, pk=None):
        chat = self.get_object()
        ser = ChatMemberSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        User = get_user_model()
        try:
            user = User.objects.get(pk=ser.validated_data['user_id'])
        except User.DoesNotExist:
            return Response({'detail': 'User not found.'}, status=status.HTTP_404_NOT_FOUND)
        chat.members.remove(user)
        return Response(status=status.HTTP_204_NO_CONTENT)

    @extend_schema(
        tags=["chat"],
        summary="Add a moderator",
        description="Owner or superuser only.",
        request=ChatMemberSerializer,
        responses={200: ChatSerializer},
    )
    @action(detail=True, methods=['post'], url_path='moderators/add')
    def add_moderator(self, request, pk=None):
        chat = self.get_object()
        if not (chat.owner == request.user or request.user.is_superuser):
            raise PermissionDenied('Only the chat owner or superuser can add moderators.')
        ser = ChatMemberSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        User = get_user_model()
        try:
            user = User.objects.get(pk=ser.validated_data['user_id'])
        except User.DoesNotExist:
            return Response({'detail': 'User not found.'}, status=status.HTTP_404_NOT_FOUND)
        chat.moderators.add(user)
        return Response(ChatSerializer(chat, context={'request': request}).data)

    @extend_schema(
        tags=["chat"],
        summary="Remove a moderator",
        description="Owner or superuser only.",
        request=ChatMemberSerializer,
        responses={204: None},
    )
    @action(detail=True, methods=['post'], url_path='moderators/remove')
    def remove_moderator(self, request, pk=None):
        chat = self.get_object()
        if not (chat.owner == request.user or request.user.is_superuser):
            raise PermissionDenied('Only the chat owner or superuser can remove moderators.')
        ser = ChatMemberSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        User = get_user_model()
        try:
            user = User.objects.get(pk=ser.validated_data['user_id'])
        except User.DoesNotExist:
            return Response({'detail': 'User not found.'}, status=status.HTTP_404_NOT_FOUND)
        chat.moderators.remove(user)
        return Response(status=status.HTTP_204_NO_CONTENT)


# ---------------------------------------------------------------------------
# Message ViewSet  (read + edit + delete only — creation is via WebSocket)
# ---------------------------------------------------------------------------

@extend_schema_view(
    list=extend_schema(tags=["chat"], summary="List messages", description="Filter by `?chat=<id>`. Chat members only."),
    retrieve=extend_schema(tags=["chat"], summary="Retrieve a message"),
    partial_update=extend_schema(tags=["chat"], summary="Edit a message", description="Sender only. Broadcasts `edit.message` to the chat channel group in real time."),
    update=extend_schema(tags=["chat"], summary="Replace a message", description="Sender only."),
    destroy=extend_schema(tags=["chat"], summary="Delete a message", description="Sender, chat owner, moderator, or superuser. Broadcasts `delete.message` to the chat channel group in real time."),
)
class MessageViewSet(viewsets.ModelViewSet):
    serializer_class = MessageSerializer
    pagination_class = CreatedCursorPagination
    filterset_fields = ['chat', 'sender', 'reply_to']
    search_fields = ['content']
    ordering_fields = ['created_at']
    ordering = ['-created_at']
    # Standard create is disabled — use POST /messages/send which handles files + WS broadcast
    http_method_names = ['get', 'patch', 'put', 'delete', 'post', 'head', 'options']

    def get_permissions(self):
        if self.action == 'destroy':
            return [CanDeleteMessage()]
        if self.action in ('update', 'partial_update'):
            return [IsMessageSenderOnly()]
        if self.action == 'send':
            return [IsAuthenticated()]
        # list, retrieve — any authenticated user (membership enforced by get_queryset)
        return [IsAuthenticated()]

    def get_queryset(self):
        user = self.request.user
        qs = Message.objects.select_related('sender', 'chat').prefetch_related('media_files', 'reactions')
        if user.is_superuser:
            return qs
        return qs.filter(Q(chat__members=user) | Q(chat__owner=user)).distinct()

    def perform_update(self, serializer):
        message = serializer.instance
        new_content = serializer.validated_data.get('content', message.content)
        changed = message.edit_content(new_content)
        if changed:
            _broadcast(message.chat_id, {
                'type': 'edit.message',
                'message_id': message.id,
                'content': message.content,
                'is_edited': True,
            })

    def perform_destroy(self, instance):
        chat_id = instance.chat_id
        message_id = instance.id
        instance.delete()
        _broadcast(chat_id, {
            'type': 'delete.message',
            'message_id': message_id,
        })

    @extend_schema(
        tags=["chat"],
        summary="Send a message",
        description=(
            "Creates a message with optional file attachments and broadcasts it to all "
            "connected WebSocket clients in the chat. Use this for all messages that include "
            "files, and optionally for text-only messages too. Chat members only."
        ),
        request=MessageSendSerializer,
        responses={201: MessageSerializer},
    )
    @action(detail=False, methods=['post'], url_path='send')
    def send(self, request):
        ser = MessageSendSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        chat = ser.validated_data['chat']
        if not request.user.is_superuser and not chat.members.filter(pk=request.user.pk).exists() and chat.owner != request.user:
            raise PermissionDenied('You are not a member of this chat.')

        message = Message.objects.create(
            chat=chat,
            sender=request.user,
            content=ser.validated_data.get('content', ''),
            reply_to=ser.validated_data.get('reply_to'),
        )

        for f in request.FILES.getlist('files'):
            ct = getattr(f, 'content_type', '')
            if ct.startswith('image/'):
                mt = 'IMAGE'
            elif ct.startswith('video/'):
                mt = 'VIDEO'
            else:
                mt = 'FILE'
            MessageFile.objects.create(message=message, file=f, media_type=mt)

        from django.conf import settings
        avatar_url = (settings.MEDIA_URL + request.user.avatar.name) if request.user.avatar else None
        media_files_data = [
            {
                'id': f.id,
                'file': settings.MEDIA_URL + f.file.name if f.file else '',
                'media_type': f.media_type,
                'uploaded_at': f.uploaded_at.isoformat(),
            }
            for f in message.media_files.all()
        ]
        _broadcast(chat.id, {
            'type': 'chat.message',
            'message_id': message.id,
            'message': message.content,
            'sender_id': request.user.id,
            'sender': request.user.username,
            'sender_avatar': avatar_url,
            'reply_to_id': message.reply_to_id,
            'media_files': media_files_data,
        })

        return Response(
            MessageSerializer(message, context={'request': request}).data,
            status=status.HTTP_201_CREATED,
        )
