#!/usr/bin/python3
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright contributors to the OpenScanHub project.

"""
Script for cron that performs the retention policy
"""
import logging
import os
import re
import shutil
from datetime import datetime, timedelta

import django
from django.db import DatabaseError
from kobo.client.constants import FINISHED_STATES

os.environ['DJANGO_SETTINGS_MODULE'] = 'osh.hub.settings'
django.setup()

from kobo.hub.models import Task  # noqa: E402

from osh.hub.scan.models import RetentionPolicySetting  # noqa: E402
from osh.hub.scan.models import TaskResultsRemoval  # noqa: E402

logger = logging.getLogger("osh.hub.scripts.osh-retention")


# Functions to determine membership for given retention setting

def is_failed(task):
    return task.is_failed()


def is_personal(task):
    # If the username does not contain /, @ or -, assume that the task is personal.
    return not re.search('[/@-]', task.owner.username)


ELIGIBILITY_FUNCTIONS = {
    # "setting name": function(task),
    "FailedStatus": is_failed,
    "PersonalScan": is_personal,
}


def delete_task_results(task, reason):
    try:
        task_dir = Task.get_task_dir(task.id)
        shutil.rmtree(task_dir)
        logger.debug('The following directory was deleted: %s', task_dir)
    except FileNotFoundError:
        # proceed silently when the task directory is missing
        pass

    except OSError as e:
        logger.error("Error deleting task directory: %s", e)
        return

    # mark the task as processed
    TaskResultsRemoval.objects.create(task=task, reason=reason)


def get_setting(name):
    try:
        return RetentionPolicySetting.objects.get(name=name)
    except RetentionPolicySetting.DoesNotExist:
        logger.warning(f"Retention policy setting ({name}) does not exist. "
                       "The affected tasks results will not be deleted.")
        return None
    except DatabaseError as e:
        logger.error("Error obtaining %s value: %s", name, e)
        raise


def has_eligible_time(task, days_delta):
    if task.dt_finished is None:
        logger.warning('Task %d is in state %s and the finish time is not set!',
                       task.id, task.get_state_display())
        return False

    return task.dt_finished + timedelta(days=days_delta) <= datetime.now()


def main():
    logger.info("Start Retention Policy Enforcement")

    # get tasks eligible for the retention policy
    eligible_tasks = Task.objects.filter(state__in=FINISHED_STATES,
                                         taskresultsremoval__isnull=True)

    # get settings from the database
    retention_settings = map(get_setting, ELIGIBILITY_FUNCTIONS.keys())

    # filter out unset settings
    retention_settings = [s for s in retention_settings if s and s.days]

    # process the tasks
    for task in eligible_tasks:
        for setting in retention_settings:
            # decide if the task should be deleted
            if has_eligible_time(task, setting.days) and \
                    ELIGIBILITY_FUNCTIONS[setting.name](task):
                delete_task_results(task, setting)
                break

    logger.info("Finish Retention Policy Enforcement")


if __name__ == '__main__':
    main()
