"""
Chat Service — WebSocket Consumers.

Consumer‌های Django Channels برای ارتباط real-time:
- ChatConsumer: ارسال/دریافت پیام، typing indicator، read receipt
- PresenceConsumer: وضعیت آنلاین/آفلاین
"""
import json
import logging
from uuid import UUID

from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer

logger = logging.getLogger('apps')


class ChatConsumer(AsyncJsonWebsocketConsumer):
    """
    WebSocket consumer برای چت.
    هر اتصال به یک کانال خاص متصل می‌شود.
    
    Events:
    - message.send: ارسال پیام جدید
    - message.edit: ویرایش پیام
    - message.delete: حذف پیام
    - typing.start: شروع تایپ
    - typing.stop: توقف تایپ
    - message.read: خوانده شدن پیام
    """

    async def connect(self):
        self.channel_id = self.scope['url_route']['kwargs']['channel_id']
        self.room_group = f"chat_{self.channel_id}"
        self.user = self.scope.get('user')

        if not self.user or self.user.is_anonymous:
            await self.close(code=4001)
            return

        # بررسی عضویت
        is_member = await self._check_membership()
        if not is_member:
            await self.close(code=4003)
            return

        await self.channel_layer.group_add(self.room_group, self.channel_name)
        await self.accept()

        # اطلاع به سایر اعضا
        await self.channel_layer.group_send(
            self.room_group,
            {
                'type': 'user.joined',
                'user_id': str(self.user.id),
                'display_name': self.user.get_full_name() or self.user.email,
            },
        )

    async def disconnect(self, close_code):
        if hasattr(self, 'room_group'):
            await self.channel_layer.group_send(
                self.room_group,
                {
                    'type': 'user.left',
                    'user_id': str(self.user.id),
                },
            )
            await self.channel_layer.group_discard(self.room_group, self.channel_name)

    async def receive_json(self, content, **kwargs):
        """دریافت پیام از کلاینت."""
        event_type = content.get('type', '')
        handler = {
            'message.send': self._handle_send_message,
            'message.edit': self._handle_edit_message,
            'message.delete': self._handle_delete_message,
            'typing.start': self._handle_typing_start,
            'typing.stop': self._handle_typing_stop,
            'message.read': self._handle_message_read,
        }.get(event_type)

        if handler:
            await handler(content)
        else:
            await self.send_json({'error': 'unknown_event', 'type': event_type})

    # ─── Handlers ────────────────────────────────────────────

    async def _handle_send_message(self, data):
        message = await self._save_message(
            content=data.get('content', ''),
            message_type=data.get('message_type', 'text'),
            reply_to_id=data.get('reply_to'),
            attachments=data.get('attachments'),
        )
        if message:
            await self.channel_layer.group_send(
                self.room_group,
                {
                    'type': 'chat.message',
                    'message': message,
                },
            )

    async def _handle_edit_message(self, data):
        message_id = data.get('message_id')
        new_content = data.get('content', '')
        message_data = await self._edit_message(message_id, new_content)
        if message_data:
            await self.channel_layer.group_send(
                self.room_group,
                {
                    'type': 'chat.message.edited',
                    'message': message_data,
                },
            )

    async def _handle_delete_message(self, data):
        message_id = data.get('message_id')
        message_data = await self._delete_message(message_id)
        if message_data:
            await self.channel_layer.group_send(
                self.room_group,
                {
                    'type': 'chat.message.deleted',
                    'message': message_data,
                },
            )

    async def _handle_typing_start(self, data):
        await self.channel_layer.group_send(
            self.room_group,
            {
                'type': 'chat.typing',
                'user_id': str(self.user.id),
                'display_name': self.user.get_full_name() or self.user.email,
                'first_name': self.user.first_name,
                'last_name': self.user.last_name,
                'email': self.user.email,
                'is_typing': True,
            },
        )

    async def _handle_typing_stop(self, data):
        await self.channel_layer.group_send(
            self.room_group,
            {
                'type': 'chat.typing',
                'user_id': str(self.user.id),
                'display_name': self.user.get_full_name() or self.user.email,
                'first_name': self.user.first_name,
                'last_name': self.user.last_name,
                'email': self.user.email,
                'is_typing': False,
            },
        )

    async def _handle_message_read(self, data):
        message_id = data.get('message_id')
        await self._mark_read(message_id)
        await self.channel_layer.group_send(
            self.room_group,
            {
                'type': 'chat.read',
                'message_id': message_id,
                'user_id': str(self.user.id),
            },
        )

    # ─── Group event senders ─────────────────────────────────

    async def chat_message(self, event):
        await self.send_json({
            'type': 'message.new',
            'message': event['message'],
        })

    async def chat_message_edited(self, event):
        await self.send_json({
            'type': 'message.edited',
            'message': event['message'],
        })

    async def chat_message_deleted(self, event):
        await self.send_json({
            'type': 'message.deleted',
            'message': event['message'],
        })

    async def chat_typing(self, event):
        # ارسال نکن به خود کاربر
        if event['user_id'] != str(self.user.id):
            event_type = 'typing.start' if event['is_typing'] else 'typing.stop'
            await self.send_json({
                'type': event_type,
                'user': {
                    'id': event['user_id'],
                    'first_name': event.get('first_name', ''),
                    'last_name': event.get('last_name', ''),
                    'email': event.get('email', ''),
                    'full_name': event['display_name'],
                },
            })

    async def chat_read(self, event):
        await self.send_json({
            'type': 'message.read',
            'message_id': event['message_id'],
            'user_id': event['user_id'],
        })

    async def user_joined(self, event):
        await self.send_json({
            'type': 'user.joined',
            'user_id': event['user_id'],
            'display_name': event.get('display_name', ''),
        })

    async def user_left(self, event):
        await self.send_json({
            'type': 'user.left',
            'user_id': event['user_id'],
        })

    # ─── Database operations ─────────────────────────────────

    @database_sync_to_async
    def _check_membership(self):
        from .services import ChannelService
        from .models import Channel
        try:
            channel = Channel.objects.get(id=self.channel_id)
            return ChannelService.is_member(channel, self.user)
        except Channel.DoesNotExist:
            return False

    @database_sync_to_async
    def _save_message(self, content, message_type, reply_to_id, attachments):
        from .services import MessageService
        from .models import Channel
        from .serializers import MessageSerializer
        try:
            channel = Channel.objects.get(id=self.channel_id)
            message = MessageService.send_message(
                tenant=self.user.tenant,
                channel=channel,
                sender=self.user,
                content=content,
                message_type=message_type,
                reply_to_id=reply_to_id,
                attachments=attachments,
            )
            return MessageSerializer(message).data
        except Exception as e:
            logger.error(f"Failed to save message: {e}")
            return None

    @database_sync_to_async
    def _edit_message(self, message_id, new_content):
        from .services import MessageService
        from .models import Message
        from .serializers import MessageSerializer
        try:
            msg = Message.objects.select_related('sender', 'reply_to').get(
                id=message_id, sender=self.user
            )
            MessageService.edit_message(msg, new_content)
            msg.refresh_from_db()
            return MessageSerializer(msg).data
        except Message.DoesNotExist:
            return None

    @database_sync_to_async
    def _delete_message(self, message_id):
        from .services import MessageService
        from .models import Message
        from .serializers import MessageSerializer
        try:
            msg = Message.objects.select_related('sender', 'reply_to').get(
                id=message_id, sender=self.user
            )
            MessageService.delete_message(msg)
            msg.refresh_from_db()
            return MessageSerializer(msg).data
        except Message.DoesNotExist:
            return None

    @database_sync_to_async
    def _mark_read(self, message_id):
        from .services import MessageService
        from .models import Channel, Message
        try:
            channel = Channel.objects.get(id=self.channel_id)
            message = Message.objects.get(id=message_id, channel=channel)
            MessageService.mark_read(
                tenant=self.user.tenant,
                channel=channel,
                user=self.user,
                message=message,
            )
        except Exception as e:
            logger.error(f"Failed to mark read: {e}")


class PresenceConsumer(AsyncJsonWebsocketConsumer):
    """
    WebSocket consumer برای حضور آنلاین.
    یک اتصال سراسری per-user (نه per-channel).
    """
    PRESENCE_GROUP = "presence"

    async def connect(self):
        self.user = self.scope.get('user')
        if not self.user or self.user.is_anonymous:
            await self.close(code=4001)
            return

        self.tenant_group = f"presence_{self.user.tenant_id}"

        await self.channel_layer.group_add(self.tenant_group, self.channel_name)
        await self.accept()

        # تنظیم آنلاین
        await self._set_online()
        await self.channel_layer.group_send(
            self.tenant_group,
            {
                'type': 'presence.update',
                'user_id': str(self.user.id),
                'status': 'online',
                'display_name': self.user.get_full_name() or self.user.email,
            },
        )

    async def disconnect(self, close_code):
        if hasattr(self, 'tenant_group'):
            await self._set_offline()
            await self.channel_layer.group_send(
                self.tenant_group,
                {
                    'type': 'presence.update',
                    'user_id': str(self.user.id),
                    'status': 'offline',
                },
            )
            await self.channel_layer.group_discard(self.tenant_group, self.channel_name)

    async def receive_json(self, content, **kwargs):
        event_type = content.get('type', '')
        if event_type == 'presence.set':
            new_status = content.get('status', 'online')
            custom = content.get('custom_status', '')
            await self._set_status(new_status, custom)
            await self.channel_layer.group_send(
                self.tenant_group,
                {
                    'type': 'presence.update',
                    'user_id': str(self.user.id),
                    'status': new_status,
                    'custom_status': custom,
                },
            )

    async def presence_update(self, event):
        if event['user_id'] != str(self.user.id):
            await self.send_json({
                'type': 'presence.update',
                'user_id': event['user_id'],
                'status': event['status'],
                'display_name': event.get('display_name', ''),
                'custom_status': event.get('custom_status', ''),
            })

    @database_sync_to_async
    def _set_online(self):
        from .services import PresenceService
        PresenceService.set_online(self.user.tenant, self.user)

    @database_sync_to_async
    def _set_offline(self):
        from .services import PresenceService
        PresenceService.set_offline(self.user.tenant, self.user)

    @database_sync_to_async
    def _set_status(self, status_val, custom=''):
        from .services import PresenceService
        PresenceService.set_status(self.user.tenant, self.user, status_val, custom)
