import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async


class ChatConsumer(AsyncWebsocketConsumer):
    """WS consumer for E2E chat — server never sees plaintext.

    Messages are persisted via REST (POST /api/messages/) which broadcasts
    the full encrypted payload here via group_send. This consumer relays
    typing indicators and read receipts, and pushes server-originated
    'chat_message' events created by the REST layer.
    """

    async def connect(self):
        self.user = self.scope.get('user')
        if not self.user or not self.user.is_authenticated:
            await self.close()
            return

        self.conversation_id = self.scope['url_route']['kwargs'].get('conversation_id')

        if not await self._is_participant(self.conversation_id, self.user.id):
            await self.close()
            return

        self.room_group_name = f'chat_{self.conversation_id}'

        await self.channel_layer.group_add(self.room_group_name, self.channel_name)
        await self.accept()

        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'user_joined',
                'user_id': str(self.user.id),
                'email': self.user.email,
            },
        )

    async def disconnect(self, close_code):
        if hasattr(self, 'room_group_name'):
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'user_left',
                    'user_id': str(self.user.id) if self.user else None,
                },
            )
            await self.channel_layer.group_discard(self.room_group_name, self.channel_name)

    async def receive(self, text_data):
        data = json.loads(text_data)
        message_type = data.get('type')

        if message_type == 'typing':
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'user_typing',
                    'user_id': str(self.user.id),
                    'email': self.user.email,
                    'is_typing': bool(data.get('is_typing', False)),
                },
            )
        elif message_type == 'read':
            await self.mark_messages_read(self.conversation_id, str(self.user.id))
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'messages_read',
                    'user_id': str(self.user.id),
                    'conversation_id': self.conversation_id,
                },
            )

    async def chat_message(self, event):
        await self.send(text_data=json.dumps({'type': 'message', 'message': event['message']}))

    async def user_joined(self, event):
        await self.send(text_data=json.dumps({
            'type': 'user_joined',
            'user_id': event['user_id'],
            'email': event['email'],
        }))

    async def user_left(self, event):
        await self.send(text_data=json.dumps({
            'type': 'user_left',
            'user_id': event['user_id'],
        }))

    async def user_typing(self, event):
        await self.send(text_data=json.dumps({
            'type': 'typing',
            'user_id': event['user_id'],
            'email': event['email'],
            'is_typing': event['is_typing'],
        }))

    async def messages_read(self, event):
        await self.send(text_data=json.dumps({
            'type': 'read',
            'user_id': event['user_id'],
            'conversation_id': event['conversation_id'],
        }))

    @database_sync_to_async
    def _is_participant(self, conversation_id, user_id):
        from .models import ConversationParticipant
        return ConversationParticipant.objects.filter(
            conversation_id=conversation_id, user_id=user_id,
        ).exists()

    @database_sync_to_async
    def mark_messages_read(self, conversation_id, user_id):
        from .models import Message
        from django.utils import timezone
        Message.objects.filter(
            conversation_id=conversation_id, is_read=False,
        ).exclude(sender_id=user_id).update(is_read=True, read_at=timezone.now())


class NotificationConsumer(AsyncWebsocketConsumer):
    """WebSocket consumer for real-time notifications."""

    async def connect(self):
        self.user = self.scope.get('user')
        if not self.user or not self.user.is_authenticated:
            await self.close()
            return
        self.room_group_name = f'notifications_{self.user.id}'
        await self.channel_layer.group_add(self.room_group_name, self.channel_name)
        await self.accept()

    async def disconnect(self, close_code):
        if hasattr(self, 'room_group_name'):
            await self.channel_layer.group_discard(self.room_group_name, self.channel_name)

    async def receive(self, text_data):
        pass

    async def notification(self, event):
        await self.send(text_data=json.dumps({
            'type': 'notification',
            'notification': event['notification'],
        }))
