#!/usr/bin/env python
# @Project ：django_base_ai
# @File    : message_center.py
# @Author  : cx
# @Time    : 11/2/2025
# @Desc    : 消息中心创建、推送与 WebSocket 推送。
import json
import logging
import math
import time
from datetime import datetime

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.core.cache import cache
from django.db.models import Q
from django.http import StreamingHttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django_celery_beat.models import CrontabSchedule, PeriodicTask
from django_restql.fields import DynamicSerializerMethodField
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated

from django_base_ai.settings import TASK_STATUS
from django_base_ai.system.models import MessageCenter, MessageCenterTargetUser
from django_base_ai.utils.json_response import DetailResponse, ErrorResponse, SuccessResponse
from django_base_ai.utils.permission import OpenApiPermission
from django_base_ai.utils.serializers import CustomModelSerializer
from django_base_ai.utils.viewset import CustomModelViewSet
from django_base_ai.websocket.websocket_config import websocket_push

logger = logging.getLogger(__name__)


class MessageCenterSerializer(CustomModelSerializer):
    """
    消息中心-序列化器
    """

    progress = DynamicSerializerMethodField()
    task_status = DynamicSerializerMethodField()
    cron = DynamicSerializerMethodField()

    def get_cron(self, instance, parsed_query):
        info = []
        periodic_tasks = instance.periodic_task.all()
        for p_task in periodic_tasks:
            info.append(
                {
                    "id": p_task.id,
                    "info": [
                        p_task.crontab.minute,
                        p_task.crontab.hour,
                        p_task.crontab.day_of_month,
                        p_task.crontab.month_of_year,
                        p_task.crontab.day_of_week,
                    ],
                }
            )
        return info

    def get_progress(self, instance, parsed_query):
        is_read_count = MessageCenterTargetUser.objects.filter(messagecenter=instance, is_read=True).count()
        total = MessageCenterTargetUser.objects.filter(messagecenter=instance).count()
        return math.floor((is_read_count / total) * 100) if total != 0 else 0

    def get_task_status(self, instance, parsed_query):
        if not instance.task_id:
            return "未发送"
        try:
            from celery.result import AsyncResult

            task = AsyncResult(instance.task_id)
            return TASK_STATUS.get(task.status, f"未知{task.status}") if task else "未找到"
        except Exception as e:
            print(e.args)
            return "未知"

    class Meta:
        model = MessageCenter
        fields = "__all__"
        read_only_fields = ["id"]


def ws_send_message(message_center_target_user_result, data, creator_name=""):
    targetuser_data = []
    create_datetime = data.create_datetime.strftime("%Y-%m-%d %H:%M:%S")
    for item in message_center_target_user_result:
        user_id = item.users.id
        targetuser_data.append(user_id)
        room_name = f"{user_id}_{cache.get(user_id, 0)}"
        websocket_push(
            room_name,
            message={
                "id": data.id,
                "msgType": "selfMessage",
                "title": data.title,
                "short_content": data.short_content,
                "thumbnail": data.thumbnail,
                "message_type": data.message_type,
                "content": data.content,
                "url": data.url,
                "target_type": data.target_type,
                "is_withdraw": data.is_withdraw,
                "ext_kwargs": data.ext_kwargs,
                "creator_name": creator_name,
                "create_datetime": create_datetime,
            },
        )
    return targetuser_data


class MessageCenterCreateUpdateSerializer(CustomModelSerializer):
    """
    消息中心-新增-序列化器
    """

    def save(self, **kwargs):
        # "* * * * *" => "{0}min {1}hour {2}day {3}month {4}week"
        instance = super().save(**kwargs)
        current_user = self.request.user
        upadate_crontab_ids = []
        cron_info = self.initial_data.get("cron", [])
        title = self.initial_data.get("title", "")
        if len(cron_info) < 1:
            return instance
        for c_info in cron_info:
            crontab_id = c_info.get("id", 0)
            cron_list = c_info.get("info", [])
            if len(cron_list) != 5:
                return instance
            if crontab_id == 0:
                # 添加任务定时器
                crontab = CrontabSchedule.objects.create(
                    minute=cron_list[0],
                    hour=cron_list[1],
                    day_of_month=cron_list[2],
                    month_of_year=cron_list[3],
                    day_of_week=cron_list[4],
                )
                periodic_task = PeriodicTask.objects.create(
                    crontab=crontab,
                    name=f"{time.time_ns()}-{title}",
                    enabled=False,
                    task="celery_tasks.tasks.send_message",
                    expires=instance.expires,
                    description=f"{current_user.username}创建",
                )
                periodic_task.args = [instance.id, current_user.id, current_user.name]
                periodic_task.save()
                upadate_crontab_ids.append(periodic_task.id)
            else:
                # 更新任务定时器
                CrontabSchedule.objects.filter(id=crontab_id).update(
                    minute=cron_list[0],
                    hour=cron_list[1],
                    day_of_month=cron_list[2],
                    month_of_year=cron_list[3],
                    day_of_week=cron_list[4],
                )
                upadate_crontab_ids.append(crontab_id)
        current_periodic_task = instance.periodic_task.all()
        current_crontab_ids = [item.id for item in current_periodic_task]
        if current_crontab_ids == upadate_crontab_ids:
            return instance
        del_ids = set(current_crontab_ids).difference(set(upadate_crontab_ids))
        if len(del_ids):
            CrontabSchedule.objects.filter(id__in=del_ids).delete()

        instance.periodic_task.set(upadate_crontab_ids)
        return instance

    class Meta:
        model = MessageCenter
        fields = "__all__"
        read_only_fields = ["id"]


def websocket_to_message(u_info, new_message_center, username):
    longth = len(u_info)
    index = 1
    for item in u_info:
        uid = item.users.id
        is_online = cache.get(uid, 0)
        room_name = f"{uid}_{is_online}"
        print(f"room_name={room_name}")
        websocket_push(
            room_name,
            message={
                "id": item.id,
                "msgType": "selfMessage",
                "title": new_message_center.title,
                "short_content": new_message_center.short_content,
                "thumbnail": new_message_center.thumbnail,
                "message_type": new_message_center.message_type,
                "content": new_message_center.content,
                "url": new_message_center.url,
                "target_type": new_message_center.target_type,
                "is_withdraw": new_message_center.is_withdraw,
                "ext_kwargs": new_message_center.ext_kwargs,
                "creator_name": username,
                "create_datetime": new_message_center.create_datetime.strftime("%Y-%m-%d %H:%M:%S"),
            },
        )
        chats = {
            "progress": 0 if longth == 0 or index == 0 else round((index / longth) * 100, 1),
            "is_end": False if index < longth else True,
        }
        yield f"data: {json.dumps(chats)}\n\n"
        index += 1


class MessageCenterViewSet(CustomModelViewSet):
    """
    消息中心接口
    list:查询
    create:新增
    update:修改
    retrieve:单例
    destroy:删除
    """

    queryset = MessageCenter.objects.order_by("-id")
    serializer_class = MessageCenterSerializer
    create_serializer_class = MessageCenterCreateUpdateSerializer
    update_serializer_class = MessageCenterCreateUpdateSerializer
    search_fields = ["title"]
    # extra_filter_backends = []

    @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated])
    def get_unread_msg(self, request):
        """获取未读消息数量"""
        self_user_id = self.request.user.id
        count = MessageCenterTargetUser.objects.filter(users__id=self_user_id, is_read=False).count()
        return DetailResponse(data={"count": count}, msg="获取成功")

    @action(methods=["POST"], detail=False, permission_classes=[IsAuthenticated])
    def send_ws_msg(self, request):

        channel_layer = get_channel_layer()

        room_name = f"user_{request.user.id}"
        send_data = request.data
        obj = {"type": "push.message", "json": send_data}
        async_to_sync(channel_layer.group_send)(room_name, obj)
        return DetailResponse()

    @action(methods=["POST"], detail=False, permission_classes=[IsAuthenticated])
    def read_one_click(self, request):
        """
        一键已读
        :param request:
        :return:
        """
        MessageCenterTargetUser.objects.filter(users=request.user).update(is_read=True)
        return DetailResponse(msg="设置成功")

    @action(methods=["POST"], detail=False, permission_classes=[IsAuthenticated])
    def resend_message(self, request):
        current_user = request.user
        # 默认按用户
        nid = request.data.get("nid", None)
        uid = request.data.get("uid", [])
        if not nid:
            return ErrorResponse(msg="参数错误")
        message_center_target_user_filter = Q()
        # message_center_target_user_filter.connector = 'OR'  # 默认为and
        new_message_center = MessageCenter.objects.filter(creator=current_user.id, id=nid).first()
        if len(uid):
            message_center_target_user_filter.children.append(("users__id__in", uid))
        u_info = (
            MessageCenterTargetUser.objects.filter(messagecenter=new_message_center)
            .filter(message_center_target_user_filter)
            .all()
        )

        return StreamingHttpResponse(
            websocket_to_message(u_info, new_message_center, current_user.username), content_type="text/event-stream"
        )

    @action(methods=["POST"], detail=False, permission_classes=[OpenApiPermission], authentication_classes=[])
    def open_api_create_message(self, request):
        """
        Open API
        :param request:
        :return: 第三方API发送通知
        """
        message_center = self.create(request)
        mid = message_center.data.get("data", {}).get("id", None)
        u_info = MessageCenterTargetUser.objects.filter(messagecenter__id=mid).all()
        websocket_to_message(u_info, message_center, "")
        return DetailResponse({"nid": mid}, "发送成功")

    @action(methods=["GET"], detail=False, permission_classes=[OpenApiPermission], authentication_classes=[])
    def open_api_get_message(self, request):
        agent_id = request.GET.get("agent_id", None)
        if not agent_id:
            return ErrorResponse("agent_id参数错误")
        queryset = MessageCenter.objects.filter(ext_kwargs__agent_id=agent_id).all()
        serializer = self.get_serializer(queryset, many=True, request=request)
        return SuccessResponse(data=serializer.data, msg="获取成功")


@csrf_exempt
def websocket_view(request):
    """
    websocket测试
    :param request:
    :return:
    """
    method = request.method.upper()
    if method == "GET":
        return render(request, "websocket.html")
    else:

        @action(methods=["POST"], detail=False, permission_classes=[])
        def send_ws_msg(self, request):
            from channels.layers import get_channel_layer

            channel_layer = get_channel_layer()
            from asgiref.sync import async_to_sync

            username = request.user.username.upper()
            nid = request.POST.get("nid", None)
            if nid is None:
                return DetailResponse({"code": 500, "msg": "缺少任务id"})
            for index in range(0, 20):
                obj = {"type": "chat_message", "message": str(datetime.now())}
                async_to_sync(channel_layer.group_send)(nid, obj)
            obj = {"type": "chat_message", "message": str(datetime.now())}
            async_to_sync(channel_layer.group_send)(username, obj)
            return DetailResponse()
