import os
from contextlib import contextmanager
from datetime import timedelta, datetime

from django.utils.translation import gettext_lazy as _

from async_tasks.models import AsyncTask


class AsyncTaskProcessor:

    def __init__(self, task_id):
        self.task = AsyncTask.objects.get(pk=task_id)
        self._has_error = False

    def ready(self):
        if self.task.status == AsyncTask.STATUS_PROCESSING:
            return False

        conflicts = AsyncTask.objects.filter(
            handler=self.task.handler,
            status=AsyncTask.STATUS_PROCESSING
        )

        if conflicts.exists():
            self.task.update(status=AsyncTask.STATUS_INLINE)
            return False

        return True

    @contextmanager
    def start_task(self):
        task = self.task
        start_time = datetime.now()

        task.update(
            percent=1,
            status=AsyncTask.STATUS_PROCESSING
        )

        try:
            yield task
        except Exception as e:
            task.update(
                status=AsyncTask.STATUS_ERROR,
                status_text=_("Error: %s") % str(e)[:200]
            )
            self._has_error = True
        finally:
            if self._has_error:
                return
            end_time = datetime.now()

            delta = end_time - start_time
            delta = delta - timedelta(microseconds=delta.microseconds)

            task.update(
                percent=100,
                status=AsyncTask.STATUS_COMPLETED,
                status_text=str(delta)
            )

            if task.file and os.path.exists(task.file.path):
                os.remove(task.file.path)

    def get_next_task_id(self):
        if self._has_error:
            return None

        next_task = (
            AsyncTask
            .objects
            .filter(
                id__gt=self.task.pk,
                status=AsyncTask.STATUS_INLINE,
                handler=self.task.handler)
            .order_by("id")
            .only("id")
            .first()
        )

        if next_task:
            return next_task.pk

        return None
