import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from django.contrib.auth import get_user_model

User = get_user_model()


class NotificationConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope.get('user')
        if not self.user or self.user.is_anonymous:
            await self.close()
            return

        self.group_name = f'notifications_{self.user.id}'
        await self.channel_layer.group_add(self.group_name, self.channel_name)
        await self.accept()

        # Send unread count on connect
        count = await self.get_unread_count()
        await self.send(text_data=json.dumps({
            'type': 'unread_count',
            'count': count
        }))

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

    async def receive(self, text_data):
        data = json.loads(text_data)
        if data.get('type') == 'mark_read':
            await self.mark_notification_read(data.get('id'))

    async def notification_message(self, event):
        """Handle notification_message from channel layer"""
        await self.send(text_data=json.dumps({
            'type': 'notification',
            'data': event['data']
        }))

    async def unread_update(self, event):
        """Handle unread count update"""
        await self.send(text_data=json.dumps({
            'type': 'unread_count',
            'count': event['count']
        }))

    @database_sync_to_async
    def get_unread_count(self):
        from apps.notifications.models import Notification
        return Notification.objects.filter(user=self.user, is_read=False).count()

    @database_sync_to_async
    def mark_notification_read(self, notif_id):
        from apps.notifications.models import Notification
        try:
            notif = Notification.objects.get(pk=notif_id, user=self.user)
            notif.is_read = True
            notif.save()
        except Notification.DoesNotExist:
            pass


class DiscussionConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope.get('user')
        if not self.user or self.user.is_anonymous:
            await self.close()
            return

        self.discussion_id = self.scope['url_route']['kwargs']['discussion_id']
        self.group_name = f'discussion_{self.discussion_id}'

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

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

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

        if msg_type == 'message':
            message = await self.save_message(data)
            if message:
                await self.channel_layer.group_send(
                    self.group_name,
                    {
                        'type': 'chat_message',
                        'data': message
                    }
                )
        elif msg_type == 'reaction':
            result = await self.toggle_reaction(data)
            if result:
                await self.channel_layer.group_send(
                    self.group_name,
                    {
                        'type': 'reaction_update',
                        'data': result
                    }
                )

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

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

    @database_sync_to_async
    def save_message(self, data):
        from apps.discussions.models import Discussion, Message
        try:
            discussion = Discussion.objects.get(pk=self.discussion_id)
            message = Message.objects.create(
                discussion=discussion,
                author=self.user,
                content=data.get('content', ''),
                parent_id=data.get('parent_id')
            )
            return {
                'id': message.id,
                'author': self.user.full_name,
                'author_initials': self.user.initials,
                'author_role': self.user.roles.first().name if self.user.roles.exists() else 'Member',
                'author_id': self.user.id,
                'content': message.content,
                'parent_id': message.parent_id,
                'is_pinned': message.is_pinned,
                'created_at': message.created_at.isoformat(),
                'reactions': [],
                'reply_count': 0
            }
        except Discussion.DoesNotExist:
            return None

    @database_sync_to_async
    def toggle_reaction(self, data):
        from apps.discussions.models import Message, MessageReaction
        try:
            message = Message.objects.get(pk=data.get('message_id'))
            emoji = data.get('emoji')
            reaction, created = MessageReaction.objects.get_or_create(
                message=message, user=self.user, emoji=emoji
            )
            if not created:
                reaction.delete()
            # Return updated reactions
            reactions = []
            for r in message.reactions.all():
                reactions.append({
                    'user': r.user.full_name,
                    'user_id': r.user.id,
                    'emoji': r.emoji
                })
            return {
                'message_id': message.id,
                'reactions': reactions
            }
        except Message.DoesNotExist:
            return None
