from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import render, get_object_or_404
from django.http import JsonResponse, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from .models import Conversation, Attachment


@staff_member_required
def chat_view(request, conversation_id):
    conversation = get_object_or_404(Conversation, id=conversation_id)
    return render(request, 'admin/live_chat/chat.html', {'conversation': conversation})


@staff_member_required
@csrf_exempt
def upload_file(request, conversation_id):
    ALLOWED_CONTENT_TYPES = ['image/jpeg', 'image/png', 'application/pdf', 'text/plain']
    MAX_FILE_SIZE = 5 * 1024 * 1024  # 5 MB

    if request.method == 'POST' and request.FILES.get('file'):
        uploaded_file = request.FILES['file']

        # Validate file type
        if uploaded_file.content_type not in ALLOWED_CONTENT_TYPES:
            return JsonResponse({'status': 'error', 'message': 'Unsupported file type.'}, status=400)

        # Validate file size
        if uploaded_file.size > MAX_FILE_SIZE:
            return JsonResponse({'status': 'error', 'message': 'File size exceeds limit.'}, status=400)

        # Save the attachment
        conversation = get_object_or_404(Conversation, id=conversation_id)
        attachment = Attachment.objects.create(
            conversation=conversation,
            file=uploaded_file
        )

        # Notify via WebSocket
        from asgiref.sync import async_to_sync
        from channels.layers import get_channel_layer
        channel_layer = get_channel_layer()
        async_to_sync(channel_layer.group_send)(
            f'chat_{conversation_id}',
            {
                'type': 'chat_message',
                'message': f"{request.user.username} sent a file.",
                'sender': request.user.username,
                'file_url': attachment.file.url,
                'file_id': attachment.id,
            }
        )

        return JsonResponse({
            'status': 'success',
            'file_url': attachment.file.url,
            'file_id': attachment.id,
            'message': f"File '{uploaded_file.name}' uploaded successfully."
        })
    return HttpResponseForbidden()

<!-- your_app_name/templates/admin/chat.html -->

{% extends "admin/base_site.html" %}
{% load static %}

{% block title %}Chat - Conversation {{ conversation.id }}{% endblock %}

{% block content %}
<h1>Conversation between {{ conversation.user.username }} and {{ conversation.operator.username }}</h1>

<div id="chat-log" style="border:1px solid #ccc; padding:10px; height:400px; overflow-y:scroll;">
    {% for msg in conversation.messages %}
        <p><strong>{{ msg.sender }}:</strong> {{ msg.content }}</p>
    {% endfor %}
    {% for attachment in conversation.attachments.all %}
        <p><strong>File:</strong> <a href="{{ attachment.file.url }}" target="_blank">Download {{ attachment.file.name }}</a></p>
    {% empty %}
        <p>No messages yet.</p>
    {% endfor %}
</div>

<textarea id="chat-message-input" style="width:100%; height:100px;" placeholder="Type your message here..."></textarea>
<br>
<input type="file" id="chat-file-input" />
<button id="chat-file-upload" class="default">Upload File</button>
<button id="chat-message-submit" class="default">Send</button>

<script>
    const conversationId = "{{ conversation.id }}";
    const userName = "{{ request.user.username }}";
    const chatLog = document.getElementById('chat-log');
    const messageInput = document.getElementById('chat-message-input');
    const messageSubmit = document.getElementById('chat-message-submit');
    const fileInput = document.getElementById('chat-file-input');
    const fileUploadButton = document.getElementById('chat-file-upload');

    const ws_scheme = window.location.protocol === "https:" ? "wss" : "ws";
    const chatSocket = new WebSocket(
        ws_scheme + '://' + window.location.host + '/ws/chat/' + conversationId + '/'
    );

    chatSocket.onmessage = function(e) {
        const data = JSON.parse(e.data);
        const message = data.message;
        const sender = data.sender;
        const fileUrl = data.file_url;
        const fileId = data.file_id;

        let msgElement = document.createElement('p');
        msgElement.innerHTML = `<strong>${sender}:</strong> ${message}`;
        chatLog.appendChild(msgElement);

        if (fileUrl) {
            let fileElement = document.createElement('p');
            // Display image previews if the file is an image
            if (fileUrl.match(/\.(jpeg|jpg|gif|png)$/)) {
                fileElement.innerHTML = `<strong>Image:</strong> <br><img src="${fileUrl}" alt="Image" style="max-width: 200px;"/>`;
            } else {
                fileElement.innerHTML = `<strong>File:</strong> <a href="${fileUrl}" target="_blank">Download File</a>`;
            }
            chatLog.appendChild(fileElement);
        }

        chatLog.scrollTop = chatLog.scrollHeight;
    };

    chatSocket.onclose = function(e) {
        console.error('Chat socket closed unexpectedly');
    };

    messageSubmit.onclick = function(e) {
        const message = messageInput.value;
        if (message.trim() !== "") {
            chatSocket.send(JSON.stringify({
                'message': message
            }));
            messageInput.value = '';
        }
    };

    // Allow sending message by pressing Enter
    messageInput.addEventListener("keyup", function(event) {
        if (event.keyCode === 13 && !event.shiftKey) {
            event.preventDefault();
            messageSubmit.click();
        }
    });

    // Handle file uploads
    fileUploadButton.onclick = function(e) {
        const file = fileInput.files[0];
        if (file) {
            const formData = new FormData();
            formData.append('file', file);

            fetch("{% url 'admin_upload_file' conversation.id %}", {
                method: 'POST',
                headers: {
                    'X-CSRFToken': '{{ csrf_token }}'
                },
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    // Optionally, notify the user of successful upload
                    console.log(data.message);
                } else {
                    console.error('File upload failed:', data.message);
                }
            })
            .catch(error => {
                console.error('Error uploading file:', error);
            });

            // Clear the file input
            fileInput.value = '';
        }
    };
</script>
{% endblock %}

# your_app_name/consumers.py

import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from .models import Conversation, Attachment

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.conversation_id = self.scope['url_route']['kwargs']['conversation_id']
        self.conversation_group_name = f'chat_{self.conversation_id}'

        # Join conversation group
        await self.channel_layer.group_add(
            self.conversation_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        # Leave conversation group
        await self.channel_layer.group_discard(
            self.conversation_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    async def receive(self, text_data):
        data = json.loads(text_data)
        message = data.get('message')
        sender = self.scope['user'].username

        if message:
            # Save message to the Conversation model
            conversation = await self.get_conversation()
            if conversation:
                conversation.messages = conversation.messages or []
                conversation.messages.append({'sender': sender, 'content': message})
                await self.save_conversation(conversation)

                # Send message to conversation group
                await self.channel_layer.group_send(
                    self.conversation_group_name,
                    {
                        'type': 'chat_message',
                        'message': message,
                        'sender': sender,
                    }
                )

    # Receive message from conversation group
    async def chat_message(self, event):
        message = event['message']
        sender = event['sender']
        file_url = event.get('file_url', None)
        file_id = event.get('file_id', None)

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            'message': message,
            'sender': sender,
            'file_url': file_url,
            'file_id': file_id,
        }))

    @database_sync_to_async
    def get_conversation(self):
        try:
            return Conversation.objects.get(id=self.conversation_id)
        except Conversation.DoesNotExist:
            return None

    @database_sync_to_async
    def save_conversation(self, conversation):
        conversation.save()

class Conversation(models.Model):
    user = models.ForeignKey(User, related_name="Chats", on_delete=models.CASCADE, null=False, blank=False)
    operator = models.ForeignKey(User, related_name="OPChats", on_delete=models.CASCADE, null=False, blank=False)
    messages = models.JSONField(default=dict, null=True, blank=True)
    created_add = models.DateTimeField(auto_now_add=True)
    last_touch = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.user}-{self.operator}"


class Attachment(models.Model):
    conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, related_name='attachments')
    file = models.FileField(upload_to='attachments/')
    uploaded_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.conversation}-file-{self.id}"

ببین خیلی خیلی خیلی زشته
خیلی خیلی زشته
اصلا یه کاری کن
صفحه اش از ادمین ارثبری نکنه
یه صفحه مشابه تلگرام بساز و سمت چپ لیست چت ها بر اساس تاریخ لیست کن و دیفالتش امروز باشه ولی بالا یه تقویم انتخاب تاریخ باشه
تو صفحه چت هم ادمین بتونه عکس فایل پی دی اف ویدیو و فایل ارسال کنه
بعد برای ادمینش هم یه حرکت بزن
باید دوتا لیست داشته باشیم
یه پروکسی بزن که بیاد اونایی که بدون اوپراتور اند رو نمایش بده و اپراتور بگه من میخوام این رو جواب بدم و بعد اوپراتورش ست بشده
درواقع هر اوپراتور بتونه فقط چت های خودش رو ببینه و فقط superuser بتونه همه چت ها رو ببینه و بتونه همه جا جواب بده
تو اطلاعات هر پیام زمان ارسالش رو هم سیو کن و امکان ریپلی پیام رو هم بزار همچنین ادمین بتونه پیام خودش رو ویرایش هم کنه
بجز این باید ادمین بتونه خاتمه چت رو هم انجام بده و بعد از اون دیگه چته امکان ادامه دادن رو نداشته باشه تو لایه فایل html
امکان ارسال ایموجی رو هم داشته باشه همچنین ارسال لوکیشن روی مپ واجب واجب هست اینم در نظر بگیر ویس هم بتونه بفرسته نور علی نور میشه
فقط برای یه محصول پروداکشن داری کد میزنی
دقت کن خطا نداشته باشی
فعلا اینو با دقت بالا بنویس
تو پیام بعدی بهت میگم براش تست کیس بنویسی 
فعلا تست رو ننویس و فقط روی چت تمرکز کن