#!/usr/bin/env python
# @Project ：django_base_ai
# @File    : base_task.py
# @Author  : cx
# @Time    : 11/2/2025
# @Desc    : 基础任务与执行历史接口
import logging
import time
from datetime import datetime, timedelta

from django_celery_beat.models import CrontabSchedule, PeriodicTask
from django_filters.rest_framework import (
    DateTimeFromToRangeFilter,
    FilterSet,
)
from django_restql.fields import DynamicSerializerMethodField
from rest_framework.decorators import action

from django_base_ai.system.models import BaseTask
from django_base_ai.utils.json_response import DetailResponse, ErrorResponse
from django_base_ai.utils.serializers import CustomModelSerializer
from django_base_ai.utils.viewset import CustomModelViewSet

logger = logging.getLogger(__name__)


class BaseTaskSerializer(CustomModelSerializer):
    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

    class Meta:
        model = BaseTask
        fields = "__all__"
        read_only_fields = ["id"]
        depth = 1


class BaseTaskCreateUpdateSerializer(CustomModelSerializer):
    def save(self, **kwargs):
        # "* * * * *" => "{0}min {1}hour {2}day {3}month {4}week"
        instance = super().save(**kwargs)
        upadate_crontab_ids = []
        cron_info = self.initial_data.get("cron", [])
        task_name = self.initial_data.get("task_name", "")
        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()}-{task_name}",
                    enabled=False,
                    task="celery_tasks.tasks.send_url",
                    expires=(datetime.now() + timedelta(days=365)).strftime("%Y-%m-%d %H:%M:%S"),
                    description="",
                )
                periodic_task.args = [instance.id, periodic_task.id]
                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 = BaseTask
        fields = "__all__"
        read_only_fields = ["id"]


class FilterBaseTask(FilterSet):
    create_datetime = DateTimeFromToRangeFilter("create_datetime")

    class Meta:
        model = BaseTask
        fields = ["task_status", "task_name", "create_datetime"]


class BaseTaskViewSet(CustomModelViewSet):
    """
    任务管理
    list:查询
    create:新增
    update:修改
    retrieve:单例
    destroy:删除
    """

    queryset = BaseTask.objects.all()
    serializer_class = BaseTaskSerializer
    create_serializer_class = BaseTaskCreateUpdateSerializer
    update_serializer_class = BaseTaskCreateUpdateSerializer
    # extra_filter_backends = []
    filter_class = FilterBaseTask
    filter_fields = ["task_status", "$task_name"]
    search_fields = ["task_name"]

    # 手动验证定时任务
    @action(methods=["post"], detail=False)
    def check_task(self, request, *args, **kwargs):
        nid = request.data.get("nid", 0)
        if not nid:
            return ErrorResponse(msg="参数不合法")
        base_task = BaseTask.objects.get(id=nid)
        if not base_task:
            return ErrorResponse(msg="任务不存在或已被禁用")

        from django_base_ai.utils import execute_task

        execute_request_task = execute_task.ExecuteRequestTask(nid)
        msg = execute_request_task.run()
        return DetailResponse(msg=f"运行成功,耗时(秒)：{int(msg)}")

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        # 删除定时器
        instance.periodic_task.all().delete()
        instance.delete()
        return DetailResponse(data=[], msg="删除成功")

    @action(methods=["put"], detail=False)
    def update_task_status(self, request, *args, **kwargs):
        nid = request.data.get("nid", 0)
        if not nid:
            return ErrorResponse(msg="参数不合法")
        current_user = request.user
        base_task = BaseTask.objects.filter(id=nid, creator=current_user.id).first()
        base_task.task_status = 0 if base_task.task_status else 1
        for periodic_task in base_task.periodic_task.all():
            periodic_task.enabled = base_task.task_status
            periodic_task.save()
        base_task.save()
        return DetailResponse(data=[], msg="操作成功")
