from random import randint

import psutil
from django.db import connection
from uwsgidecoratorsfallback import cron

from ocyan.main.utils import run_exclusive

from .choices import TaskStatus
from .models import Task


def check_task_progress():
    """
    Checks for tasks that are marked as 'in progress' but whose process has been terminated.
    If the task's process is no longer running, its status is updated to 'failed'.
    """

    def not_in_progress(t):
        return t.pid and (not psutil.pid_exists(t.pid))

    tasks = filter(not_in_progress, Task.objects.filter(status=TaskStatus.PROGRESS))

    for task in tasks:
        task.set_status(
            TaskStatus.FAILED,
            "The task has been terminated, the process has been stopped",
        )


@cron(-1, -1, -1, -1, -1, target="spooler")  # every minute
def check_task_retry(sender, **kwargs):  # pylint:disable=unused-argument
    try:
        if not connection.is_usable():
            connection.close()
    except AttributeError:
        connection.close_if_unusable_or_obsolete()

    check_task_progress()

    tasks = Task.objects.retryable()

    for task in tasks:
        task.run()


@cron(
    randint(0, 59), 22, -1, -1, -1, target="spooler"
)  # at a random minute around 22PM
def cleanup_old_tasks(args):  # pylint: disable=unused-argument
    # the spooler is forked so the db connection might be closed
    try:
        if not connection.is_usable():
            connection.close()
    except AttributeError:
        connection.close_if_unusable_or_obsolete()

    run_exclusive("cleanup_old_tasks", 4)
