"""
Chat Service — Business Logic Services.

سرویس‌های اصلی:
- ChannelService: مدیریت کانال‌ها
- MessageService: مدیریت پیام‌ها
- PresenceService: وضعیت حضور آنلاین
"""
import logging
from typing import Optional
from uuid import UUID

from django.db import transaction
from django.db.models import Q, F
from django.utils import timezone

from .models import (
    Channel,
    ChannelMember,
    ChannelType,
    ChannelVisibility,
    MemberRole,
    MemberStatus,
    Message,
    MessageAttachment,
    MessageReadReceipt,
    MessageType,
    PresenceStatus,
    UserPresence,
)

logger = logging.getLogger('apps')


# ══════════════════════════════════════════════════════════════
# Channel Service
# ══════════════════════════════════════════════════════════════

class ChannelService:
    """مدیریت کانال‌ها."""

    @staticmethod
    def list_user_channels(user, tenant):
        """لیست کانال‌های کاربر."""
        channel_ids = ChannelMember.objects.filter(
            user=user,
            tenant=tenant,
            status__in=[MemberStatus.ACTIVE, MemberStatus.MUTED],
        ).values_list('channel_id', flat=True)

        return Channel.objects.filter(
            id__in=channel_ids,
            is_archived=False,
        ).select_related('created_by')

    @staticmethod
    @transaction.atomic
    def create_channel(tenant, user, name, channel_type, visibility=ChannelVisibility.PRIVATE,
                       description='', member_ids=None, source_type='', source_id=None):
        """ایجاد کانال جدید."""
        channel = Channel.objects.create(
            tenant=tenant,
            name=name,
            channel_type=channel_type,
            visibility=visibility,
            description=description,
            source_type=source_type,
            source_id=source_id,
            created_by=user,
        )

        # ایجادکننده = مالک
        ChannelMember.objects.create(
            tenant=tenant,
            channel=channel,
            user=user,
            role=MemberRole.OWNER,
            status=MemberStatus.ACTIVE,
        )

        # اضافه کردن اعضا
        if member_ids:
            from apps.core.auth.models import User
            members = User.objects.filter(id__in=member_ids, tenant=tenant)
            for member in members:
                if member.id != user.id:
                    ChannelMember.objects.create(
                        tenant=tenant,
                        channel=channel,
                        user=member,
                        role=MemberRole.MEMBER,
                        status=MemberStatus.ACTIVE,
                    )

        return channel

    @staticmethod
    @transaction.atomic
    def get_or_create_direct(tenant, user1, user2):
        """دریافت یا ایجاد چت خصوصی بین دو کاربر."""
        # جستجوی کانال DM موجود
        dm_channels = Channel.objects.filter(
            tenant=tenant,
            channel_type=ChannelType.DIRECT,
            members__user=user1,
        ).filter(
            members__user=user2,
        )
        if dm_channels.exists():
            return dm_channels.first()

        # ایجاد کانال جدید
        channel = Channel.objects.create(
            tenant=tenant,
            name='',
            channel_type=ChannelType.DIRECT,
            visibility=ChannelVisibility.PRIVATE,
            max_members=2,
            created_by=user1,
        )
        ChannelMember.objects.bulk_create([
            ChannelMember(
                tenant=tenant, channel=channel, user=user1,
                role=MemberRole.MEMBER, status=MemberStatus.ACTIVE,
            ),
            ChannelMember(
                tenant=tenant, channel=channel, user=user2,
                role=MemberRole.MEMBER, status=MemberStatus.ACTIVE,
            ),
        ])
        return channel

    @staticmethod
    def update_channel(channel, **kwargs):
        """بروزرسانی کانال."""
        allowed = ['name', 'description', 'visibility', 'max_members', 'is_archived']
        for key, val in kwargs.items():
            if key in allowed:
                setattr(channel, key, val)
        channel.save()
        return channel

    @staticmethod
    def add_member(tenant, channel, user, role=MemberRole.MEMBER):
        """اضافه کردن عضو به کانال."""
        member, created = ChannelMember.objects.get_or_create(
            tenant=tenant,
            channel=channel,
            user=user,
            defaults={'role': role, 'status': MemberStatus.ACTIVE},
        )
        if not created and member.status == MemberStatus.LEFT:
            member.status = MemberStatus.ACTIVE
            member.role = role
            member.save(update_fields=['status', 'role', 'updated_at'])
        return member

    @staticmethod
    def remove_member(channel, user):
        """خروج عضو از کانال."""
        ChannelMember.objects.filter(
            channel=channel, user=user,
        ).update(status=MemberStatus.LEFT, updated_at=timezone.now())

    @staticmethod
    def get_members(channel):
        """لیست اعضای فعال کانال."""
        return ChannelMember.objects.filter(
            channel=channel,
            status__in=[MemberStatus.ACTIVE, MemberStatus.MUTED],
        ).select_related('user')

    @staticmethod
    def is_member(channel, user) -> bool:
        """بررسی عضویت کاربر در کانال."""
        return ChannelMember.objects.filter(
            channel=channel,
            user=user,
            status__in=[MemberStatus.ACTIVE, MemberStatus.MUTED],
        ).exists()


# ══════════════════════════════════════════════════════════════
# Message Service
# ══════════════════════════════════════════════════════════════

class MessageService:
    """مدیریت پیام‌ها."""

    @staticmethod
    @transaction.atomic
    def send_message(tenant, channel, sender, content,
                     message_type=MessageType.TEXT,
                     reply_to_id=None, attachments=None):
        """ارسال پیام جدید."""
        reply_to = None
        if reply_to_id:
            reply_to = Message.objects.filter(
                id=reply_to_id, channel=channel,
            ).first()

        message = Message.objects.create(
            tenant=tenant,
            channel=channel,
            sender=sender,
            content=content,
            message_type=message_type,
            reply_to=reply_to,
        )

        # پیوست‌ها — accepts list of dicts OR list of UUID strings
        if attachments:
            from apps.services.file_service.models import StoredFile
            for att in attachments:
                if isinstance(att, dict):
                    # dict format: {file_id, file_name, file_size, mime_type}
                    MessageAttachment.objects.create(
                        tenant=tenant,
                        message=message,
                        stored_file_id=att['file_id'],
                        file_name=att.get('file_name', ''),
                        file_size=att.get('file_size', 0),
                        mime_type=att.get('mime_type', ''),
                    )
                else:
                    # bare UUID string — resolve metadata from StoredFile
                    file_id = str(att)
                    try:
                        sf = StoredFile.objects.get(id=file_id)
                        MessageAttachment.objects.create(
                            tenant=tenant,
                            message=message,
                            stored_file_id=sf.id,
                            file_name=sf.original_name or '',
                            file_size=sf.size or 0,
                            mime_type=sf.mime_type or '',
                        )
                    except StoredFile.DoesNotExist:
                        MessageAttachment.objects.create(
                            tenant=tenant,
                            message=message,
                            stored_file_id=file_id,
                            file_name='',
                            file_size=0,
                            mime_type='',
                        )

        # بروزرسانی کانال
        channel.last_message_at = message.created_at
        channel.message_count = F('message_count') + 1
        channel.save(update_fields=['last_message_at', 'message_count'])

        # افزایش unread اعضای دیگر
        ChannelMember.objects.filter(
            channel=channel,
            status__in=[MemberStatus.ACTIVE, MemberStatus.MUTED],
        ).exclude(
            user=sender,
        ).update(unread_count=F('unread_count') + 1)

        return message

    @staticmethod
    def get_messages(channel, before=None, limit=50):
        """دریافت پیام‌های کانال (scroll pagination)."""
        qs = Message.objects.filter(
            channel=channel,
            is_deleted=False,
        ).select_related('sender', 'reply_to').prefetch_related('attachments')

        if before:
            # Accept either UUID (message_id) or datetime string
            try:
                from uuid import UUID as UUIDType
                UUIDType(str(before))
                # It's a UUID — look up the message to get its created_at
                try:
                    ref_msg = Message.objects.get(id=before, channel=channel)
                    qs = qs.filter(created_at__lt=ref_msg.created_at)
                except Message.DoesNotExist:
                    pass
            except (ValueError, AttributeError):
                # It's a datetime string — use directly
                qs = qs.filter(created_at__lt=before)

        return qs.order_by('-created_at')[:limit]

    @staticmethod
    def edit_message(message, new_content):
        """ویرایش پیام."""
        message.content = new_content
        message.is_edited = True
        message.edited_at = timezone.now()
        message.save(update_fields=['content', 'is_edited', 'edited_at'])
        return message

    @staticmethod
    def delete_message(message):
        """حذف نرم پیام."""
        message.is_deleted = True
        message.deleted_at = timezone.now()
        message.content = ''
        message.save(update_fields=['is_deleted', 'deleted_at', 'content'])
        return message

    @staticmethod
    def mark_read(tenant, channel, user, message):
        """علامت خوانده‌شدن پیام."""
        # ایجاد read receipt
        MessageReadReceipt.objects.get_or_create(
            tenant=tenant,
            message=message,
            user=user,
        )
        # بروزرسانی عضویت
        ChannelMember.objects.filter(
            channel=channel,
            user=user,
        ).update(
            last_read_message=message,
            last_read_at=timezone.now(),
            unread_count=0,
        )

    @staticmethod
    def search_messages(channel, query, limit=50):
        """جستجو در پیام‌های کانال."""
        return Message.objects.filter(
            channel=channel,
            is_deleted=False,
            content__icontains=query,
        ).select_related('sender').order_by('-created_at')[:limit]


# ══════════════════════════════════════════════════════════════
# Presence Service
# ══════════════════════════════════════════════════════════════

class PresenceService:
    """مدیریت حضور آنلاین."""

    @staticmethod
    def set_online(tenant, user):
        """تنظیم وضعیت آنلاین."""
        UserPresence.objects.update_or_create(
            tenant=tenant,
            user=user,
            defaults={'status': PresenceStatus.ONLINE},
        )

    @staticmethod
    def set_offline(tenant, user):
        """تنظیم وضعیت آفلاین."""
        UserPresence.objects.filter(
            tenant=tenant, user=user,
        ).update(status=PresenceStatus.OFFLINE, last_seen=timezone.now())

    @staticmethod
    def set_status(tenant, user, status_value, custom_status=''):
        """تنظیم وضعیت سفارشی."""
        UserPresence.objects.update_or_create(
            tenant=tenant,
            user=user,
            defaults={
                'status': status_value,
                'custom_status': custom_status,
            },
        )

    @staticmethod
    def get_online_users(tenant, user_ids=None):
        """دریافت کاربران آنلاین."""
        qs = UserPresence.objects.filter(
            tenant=tenant,
            status__in=[PresenceStatus.ONLINE, PresenceStatus.AWAY, PresenceStatus.BUSY],
        )
        if user_ids:
            qs = qs.filter(user_id__in=user_ids)
        return qs.select_related('user')

    @staticmethod
    def get_user_presence(user):
        """دریافت وضعیت حضور یک کاربر."""
        try:
            return UserPresence.objects.get(user=user)
        except UserPresence.DoesNotExist:
            return None
